Spring Boot/Test Code

[Spring] MockMvc를 이용한 Controller의 Test Code(JUnit5) 작성하기

sbs1621 2022. 8. 10. 18:00

간단하게 작성할 수 있는 게시판 Controller에 대한 테스트코드입니다.

테스트코드 추가하기

컨트롤러에서 cmd + shift + t(윈도우는 control + shift + t)를 누르시면 간단하게 테스트코드를 만들 수 있습니다.

Build.gradle

아래의 의존성을 주입되어있는지 확인해줍니다.

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'

어노테이션

@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class BoardControllerTest {
    ...
}
  • @ExtendWith(SpringExtension.class)
    • Spring TestContext Framework를 Junit5에 포함시킬 수 있습니다.
  • @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
    • @SpringBootTest 의 webEnvironment 는 기본적으로 SpringBootTest.WebEnvironment.MOCK 으로 설정되어 있습니다. 내장 톰캣을 구동하지 않고 임의로 빈을 만들어 테스트를 한다는 의미입니다.

테스트코드

MockMvc mockMvc;

@Autowired
BoardRepository boardRepository;

private ObjectMapper objectMapper = new ObjectMapper();

@Autowired
private WebApplicationContext wac;

@BeforeEach
public void setup() {
    this.mockMvc = MockMvcBuilders.webAppContextSetup(wac)
            .addFilter(new CharacterEncodingFilter("UTF-8", true))
            .apply(SecurityMockMvcConfigurers.springSecurity())
            .build();
}
  • @BeforeEach
    • TestCode를 실행하기 전에 미리 실행합니다.
    • Spring이 주입한 모든 객체를 사용합니다.
    • 시큐리티에 대한 설정도 추가합니다.
  • ObjectMapper
    • Text Json을 오브젝트로, 혹은 오브젝트를 Text Json으로 변경해줍니다.
    • 요청이 오면 Content-Type이 json인 것을 Object로 바꿔주고 처리 후 Object를 json으로 변경하여 request 합니다.

게시글 조회

@Test
@DisplayName("게시글 전체 조회 Test")
@WithMockUser
public void 게시글_전체_조회() throws Exception{
    this.mockMvc
            .perform(get("/api/v1/board"))
            .andExpect(status().isOk())
            .andDo(print());
}

@Test
@DisplayName("게시글 전체 조회 실패 Test")
public void 게시글_전체_조회_실패() throws Exception{
    this.mockMvc
            .perform(get("/api/v1/board"))
            .andExpect(status().is4xxClientError())
            .andDo(print());
}

게시글 작성

@Test
@DisplayName("게시글 작성 Test")
@WithMockUser(roles = "GUEST")
public void 게시글_작성() throws Exception {
    BoardDto boardDto = new BoardDto();
    boardDto.setAuthor("작성자");
    boardDto.setContent("내용");
    boardDto.setTitle("제목");

    this.mockMvc
            .perform(post("/api/v1/board")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(objectMapper.writeValueAsString(boardDto))
            )
            .andExpect(status().isOk())
            .andDo(print());
}

@Test
@DisplayName("게시글 작성 실패 Test")
public void 게시글_작성_실패() throws Exception {
    BoardDto boardDto = new BoardDto();
    boardDto.setAuthor("작성자");
    boardDto.setContent("내용");
    boardDto.setTitle("제목");
    this.mockMvc
            .perform(post("/api/v1/board")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(objectMapper.writeValueAsString(boardDto))
            )
            .andExpect(status().is4xxClientError())
            .andDo(print());
}

게시글 수정

@Test
@DisplayName("게시글 수정 Test")
@WithMockUser(roles = "GUEST")
public void 게시글_수정() throws Exception{

    Map<String, String> input = new HashMap<>();
    input.put("content", "수정된 게시글");
    input.put("title", "수정된 제목");

    mockMvc.perform(put("/api/v1/board/2")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(objectMapper.writeValueAsString(input)))
            .andExpect(status().isOk())
            .andDo(print());


}
@Test
@DisplayName("게시글 수정 실패 Test")
public void 게시글_수정_실패() throws Exception{
    Map<String, String> input = new HashMap<>();
    input.put("content", "수정된 게시글");
    input.put("title", "수정된 제목");
    this.mockMvc
            .perform(put("/api/v1/board/100")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(objectMapper.writeValueAsString(input)))
            .andExpect(status().is4xxClientError())
            .andDo(print());
}

게시글 삭제

@Test
@DisplayName("게시글 삭제 Test")
@WithMockUser(roles = "GUEST")
public void 게시글_삭제() throws Exception{
    Board board = Board.builder()
            .author("작성자")
            .title("제목")
            .content("본문")
            .build();
    boardRepository.save(board);
    this.mockMvc
            .perform(delete("/api/v1/board/1"))
            .andExpect(status().isOk())
            .andDo(print());
}

@Test
@DisplayName("게시글 삭제 실패 Test")
public void 게시글_삭제_실패() throws Exception{
    this.mockMvc
            .perform(delete("/api/v1/board/100"))
            .andExpect(status().is4xxClientError())
            .andDo(print());
}

mockMvc.perform에서 Test하는 HTTP메소드와 경로를 입력하고 status의 상태가 설정한 상태와 같다면 테스트를 통과시킵니다.

각 유닛Test끼리 사이드이팩트가 발생하지 않도록 잘 고려해서 작성해야 합니다.