a. @WebMvcTest
설명 : Spring MVC Component(@Controller, @ControllerAdice 등, @Service, @Component, @Repository 빈은 제외된다.) 이 애노테이션을 사용하게 되면 Spring Security와 MockMvc가 자동으로 설정된다. Controller빈과 연계된 클래스들을 사용하기 위해서는 @Import와 @MockBean을 사용해주어야 한다.
사용 예시
1 . 먼저 테스트하고자 하는 컨트롤러와 서비스, MockMvc를 등록해준다.
@WebMvcTest(ArticleController.class)
class ArticleControllerTest{
//테스트를 수행해 줄 MockMvc를 선언한다.
@Autowired MockMvc mvc;
//컨트롤러에 대한 단위테스트이므로 서비스 코드의 영향을 없애기 위해 가짜 객체를 만들어준다.
/*MockBean 대상 객체는 스프링 컨테이너에 의해 관리된다.
따라서 @SpringBootTest와 같이 스프링 컨테이너를 이용하는 경우에는 @MockBean을 사용하고 그렇지 않은 경우에는 @Bean을 사용하면 된다. */
@MockBean ArticleService articleService;
}
2 . 테스트 코드 메소드를 작성해 준다. 기본적으로 perfor()메소드 내에 애플리케이션에 대해 하고자 하는 행위(GET, POST 등을 이용한 값 전달)를 작성하고, andExpect()를 이용해 기대하고자 하는 값이 정상적으로 나오는지 검증한다. 마지막으로는 .andDo()를 이용해 결과값 출력 등을 해볼 수도 있다.
@WebMvcTest(ArticleController.class)
class ArticleControllerTest{
@Autowired MockMvc mvc;
@MockBean ArticleService articleService;
@Test
@DisplayName("[GET]특정 게시글 조회 테스트(게시글 O)")
public void getArticleTest() throws Exception {
Long articleId = 1L;
//controller테스트에서 service레이어의 영향을 받고 싶지 않으므로 stubbing처리를 해준다.
given(articleService.getArticle(articleId)).willReturn(new ArticleDto(articleId, "title", "content"));
// /artices/{id}에 대한 get메소드를 호출했을 때 200ok 및 응답으로 오는 json객체에 대한 값 검증을 andExpext에서 진행한다.
mvc.perform(get("/articles/" + articleId))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$..['title']").value("title"))
.andExpect(jsonPath("$..['content']").value("content"));
}
}
3 . POST방식의 경우 파라미터를 별도로 전달해주어야 하기 때문에 추가 작업이 필요하다. 이때 MultiValueMap
이라는 스프링 제공 자료구조를 사용해야 한다.(키의 중복을 허용하는 key-value 형 자료구조) andExpect()구문은 GET방식과 다르지 않다.
@WebMvcTest(ArticleController.class)
class ArticleControllerTest{
@Autowired MockMvc mvc;
@MockBean ArticleService articleService;
@Test
@DisplayName("[POST]게시글 생성 테스트(정상)")
public void createArticleTest() throws Exception{
MultiValueMap<String, String> param = new LinkedMultiValueMap<>();
given(articleService.createArticle(any(ArticleDto.class)))
.willReturn(new ArticleDto(2L, "title", "content"));
//request body에 title=제목&content=내용 형식이 담긴다고 가정했을 때 아리와 같이 입력을 해주면 된다.
param.add("title", "title");
param.add("content", "content");
mvc.perform(post("/articles/form")
.params(param))
.andExpect(status().isCreated())
.andExpect(content().string("게시글이 생성되었습니다."))
.andExpect(header().string("Location", "/articles/2"))
.andDo(print());
}
}
컨트롤러의 경우 사용자와 직접적으로 상호작용이 있는 레이어이기ㅣ 때문에 다양한 사용자 입력값에 대한 검증이 필요하다. 이런 경우 사용해 볼 수 있는 방법이 @ParameterizedTest
이다. @ParameterizedTest
를 사용한 경우에는 @Test
를 빼주어야 한다. 이번에는 정의한 메소드의 반환값을 테스트 입력으로 사용하는 @MethodSource
를 이용하여 테스트를 진행해 보았다. @MethodSource
내에 별도 값을 지정하지 않으면 테스트 메소드와 동일한 이름의 MethodSource를 찾는다.
@WebMvcTest(ArticleController.class)
class ArticleControllerTest{
@Autowired MockMvc mvc;
@MockBean ArticleService articleService;
@DisplayName("[POST]게시글 생성 테스트(입력값 오류)")
@MethodSource("createArticleFailTest") //MethodSource를 지정해 주었다.
@ParameterizedTest(name = "[{index}] message : {2}") //반환하는 인자값을 테스트명으로 사용하기 위해 name인자값을 주었다. {2}는 arguments의 3번째 인자라는 의미이다.
public void createArticleFailTest(String title, String content, String message) throws Exception {
MultiValueMap<String, String> param = new LinkedMultiValueMap<>();
//MethodSource가 반환해주는 arguments가 순서대로 title, content, message에 담기므로 이를 메소드 내에서 적절히 사용하면 된다.
param.add("title", title);
param.add("content", content);
mvc.perform(post("/articles/form")
.params(param))
.andExpect(status().isBadRequest())
.andDo(print());
}
/*입력으로 사용할 MethodSource는 static 메소드이며 반환값은 Stream<Arguments>로 선언해준다.
적절한 입력값을 생성한 뒤 Stream.of(arguments(인자값)) 형식으로 반환해준다.
*/
static Stream<Arguments> createArticleFailTest(){
StringBuilder sb = new StringBuilder();
for(int i = 0; i <= 1000; i++){
sb.append("a");
}
String largeContent = sb.toString();
return Stream.of(
arguments(null, null, "입력값 없음"),
arguments("title", null, "내용 없음"),
arguments(null, "content", "제목 없음"),
arguments("title", largeContent, "내용 1000자 초과")
);
}
@WebMvcTest(테스트 Controller)
선언MvcMock
@MockBean
선언@WebMvcTest(ArticleController.class)
class ArticleControllerTest{
//테스트를 수행해 줄 MockMvc를 선언한다.
@Autowired MockMvc mvc;
//컨트롤러에 대한 단위테스트이므로 서비스 코드의 영향을 없애기 위해 가짜 객체를 만들어준다.
/*MockBean 대상 객체는 스프링 컨테이너에 의해 관리된다.
따라서 @SpringBootTest와 같이 스프링 컨테이너를 이용하는 경우에는 @MockBean을 사용하고 그렇지 않은 경우에는 @Bean을 사용하면 된다. */
@MockBean ArticleService articleService;
}