JUnit5 REST API 테스트코드 작성

dragonappear·2022년 2월 10일
1

TDD

목록 보기
4/5
post-thumbnail

출처:

제목: "스프링부트에서 REST API 테스트코드 작성하기"
작성자: tistory(gonyda)
수정일: 2020년9월14일
링크: https://gonyda.tistory.com/14
작성일: 2022년2월11일


컨트롤러

@RequiredArgsConstructor
@RestController
@RequestMapping("api/board")
public class BoardApiController {

    private final BoardRepository boardRepository;

    @PostMapping
    public Long save(@RequestBody BoardDto dto) {
        Board board = dto.toEntity();
        return boardRepository.save(board).getId();
    }

    @Transactional
    @PutMapping("/{id}")
    public Long update(@PathVariable Long id, @RequestBody BoardDto dto) {
        Board board = boardRepository.findById(id).orElse(null);

        Long updateID = dto.update(board);
        return updateID;
    }

    @Transactional
    @DeleteMapping("/{id}")
    public Long delete(@PathVariable Long id) {
        boardRepository.deleteById(id);
        return id;
    }
}
  • 공부용이니까 서비스 클래스 만들기 귀찮아서@Transactional 컨트롤러에 걸었다.

테스트 코드

@Transactional
@Rollback
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class BoardApiControllerTest {
    @LocalServerPort
    private int port;

    @Autowired
    private TestRestTemplate restTemplate;

    @Autowired
    private BoardRepository boardRepository;

    @AfterEach
    public void clean() {
        boardRepository.deleteAll();
    }

    @DisplayName("게시글 저장 테스트")
    @Test
    void saveBoard(){
        //given
        String title = "테스트 제목";
        String content = "테스트 본문";
        String author = "테스트 계정";

        BoardDto dto = new BoardDto(title, content, author);
        String url = "http://localhost:"+port+"/api/board";

        //when
        ResponseEntity<Long> response = restTemplate.postForEntity(url, dto, Long.class);

        //then
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(response.getBody()).isGreaterThan(0L);

        List<Board> list = boardRepository.findAll();

        assertThat(list.get(0).getAuthor()).isEqualTo(author);
        assertThat(list.get(0).getTitle()).isEqualTo(title);
        assertThat(list.get(0).getContent()).isEqualTo(content);
    }

    @DisplayName("게시글 수정 테스트")
    @Test
    void modifyBoard(){
        //given
        Board saved = boardRepository.save(Board.builder()
                .title("title")
                .content("content")
                .author("author")
                .build());

        Long id = saved.getId();

        String updatedTitle = "title1";
        String updatedContent = "content1";

        BoardDto boardDto = BoardDto.builder()
                .title(updatedTitle)
                .content(updatedContent)
                .build();

        String url = "http://localhost:"+port+"/api/board/"+id;

        HttpEntity<BoardDto> requestEntity = new HttpEntity<>(boardDto);

        //when
        ResponseEntity<Long> responseEntity = restTemplate.exchange(url, HttpMethod.PUT, requestEntity, Long.class);

        //then
        assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(responseEntity.getBody()).isGreaterThan(0L);

        List<Board> list = boardRepository.findAll();
        assertThat(list.get(0).getTitle()).isEqualTo(updatedTitle);
        assertThat(list.get(0).getContent()).isEqualTo(updatedContent);
    }

    @DisplayName("게시글 삭제 테스트")
    @Test
    void deleteTest(){
        //given
        Board saved = boardRepository.save(Board.builder()
                .title("title")
                .content("content")
                .author("author")
                .build());

        //when
        String url = "http://localhost:"+port+"/api/board/"+saved.getId();
        ResponseEntity<Long> responseEntity = restTemplate.exchange(url, HttpMethod.DELETE, null, Long.class);
        //then
        assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(responseEntity.getBody()).isGreaterThan(0L);

        List<Board> deleted = boardRepository.findAll();
        assertThat(deleted).isEmpty();

    }

}

코드 분석

SpringBootTest.WebEnvironment.RANDOM_PORT

  • spring-boot-test 의존성을 추가하면 @SpringBootTest라는 어노테이션을 사용할 수 있다

  • 이 어노테이션을 사용하면 테스트에서 사용할 ApplicaitonContext를 쉽게 생성하고조작할 수 있다.

  • 기존 Spring-test에서 사용하던 @ContextConfiguration의 발전된 기능이라고 할 수 있다

  • @SpringBootTest는 매우 다양한 기능들을 제공한다.

    • 전체 빈 중 특정 빈을 선택하여 생성한다든지, 특정 빈을 Mock으로 대체한다든지, 테스트에 사용할 프로퍼티 파일을 선택하거나 특정 속성만 추가한다던지, 특정 Configuration을 선택하여 설정할 수도 있다.

    • 또한, 주요 기능으로 테스트 웹 환경을 자동으로 설정해주는 기능이 있다.

WebEnvironment.RANDOM_PORT

  • @SpringBootTestWebEnvironment 속성은 테스트 웹 환경을 설정하는 속성이며, 기본값은SpringBootTest.WebEnvironment.MOCK이다.

  • WebEnvironment.MOCK은 실제 서블릿 컨테이너를 띄우지 않고 서블릿 컨테이너를 mocking 한 것이 실행된다.

    • 이 속성 값을 사용할때는 보통 MockMvc를 주입받아 테스트한다.
  • 스프링 부트의 내장 서버를 랜덤 포트로 띄우려면 WebEnvironment.RANDOM_PORT로 설정하면 된다.

    • 이 설정은 실제로 테스트를 위한 서블릿 컨테이너를 띄운다.
    • WebEnvironment.MOCK을 사용할 때와는 달리 TestRestTemplate를 주입받아 테스트한다

TestRestTemplate

  • @SpringBootTestTestRestTemplate 을 사용한다면 편리하게 웹 통합 테스트를 할 수 있다

  • TestRestTemplate은 이름에서 알 수 있듯이 RestTemplate의 테스트를 위한 버전이다

  • @SpringBootTest에서 Web Environment설정을 하였다면 TestRestTemplate은 그에 맞춰서 자동으로 설정되어 빈이 생성된다

0개의 댓글