@WebMvcTest
- MVC를 위한 테스트, 컨트롤러가 예상대로 동작하는지 테스트하는데 사용된다.
- 웹상에서 요청과 응답에 대해 테스트할 수 있음.
- 시큐리티, 필터까지 자동으로 테스트하며, 수동으로 추가/삭제 가능.
- @SpringBootTest 어노테이션보다 가볍게 테스트할 수 있음.
- @WebMvcTest 어노테이션을 사용시 다음 내용만 스캔 하도록 제한한다.
- @Controller
- @ControllerAdvice
- @JsonComponent
- Converter
- GenericConverter
- Filter
- HandlerInterceptor
- WebMvcConfigurer
- HandlerMethodArgumentResolver
 
- MockBean, MockMVC를 자동 구성하여 테스트 가능하도록 한다.
- Spring Security의 테스트도 지원 한다. -> 로그인, 로그아웃, 세션, 필터까지 테스트할 수 있다.
- @WebMvcTest를 사용하기 위해 테스트할 특정 컨트롤러 클래스를 명시 하도록 한다.
- 예시는 아래와 같다.
@WebMvcTest(TestRestController.class) 
@Slf4j 
class TestRestControllerTest { 
    @Autowired MockMvc mvc; 
    @MockBean 
    private TestService testService;
    protected MediaType contentType =
            new MediaType(MediaType.APPLICATION_JSON.getType(),
                    MediaType.APPLICATION_JSON.getSubtype(),
                    StandardCharsets.UTF_8); 
    @Test void getListTest() throws Exception { 
        //given 
        TestVo testVo = TestVo.builder() 
            .id("hyunwook") 
            .name("현욱") 
            .build(); 
        //given 
        given(testService.selectOneMember("hyunwook")) 
            .willReturn(testVo); 
        //when 
        final ResultActions actions = mvc.perform(get("/testValue2") 
                .contentType(contentType)) 
                .andDo(print()); 
        //then 
        actions 
            .andExpect(status()
            .isOk()) 
            .andExpect(content().contentType(contentType)) 
            .andExpect(jsonPath("$.name", is("현욱"))) 
            .andDo(print()); 
    } 
}
- given에서 해당 객체를 생성 처리한다.
- when에서 목객체를 기반으로 미리 정의된 객체를 반환 처리한다.
- 선언해둔 contentType 을 지정해 준다.
 
- then에서 해당 객체의 응답값을 검사 처리한다.
 
 
 
 
코드 예시
@WebMvcTest(DMakerController.class)
@MockBean(JpaMetamodelMappingContext.class)
class DMakerControllerTest {
    @Autowired
    private MockMvc mockMvc;
    @MockBean
    private DMakerService dMakerService;
    protected MediaType contentType =
            new MediaType(MediaType.APPLICATION_JSON.getType(),
                    MediaType.APPLICATION_JSON.getSubtype(),
                    StandardCharsets.UTF_8);
    @Test
    void getAllDevelopers() throws Exception {
        //given
        DeveloperDto seniorBEDeveloper = DeveloperDto.builder()
                .developerSkillType(DeveloperSkillType.BACK_END)
                .developerLevel(DeveloperLevel.SENIOR)
                .memberId("memberId")
                .build();
        DeveloperDto juniorFEDeveloper = DeveloperDto.builder()
                .developerSkillType(DeveloperSkillType.FRONT_END)
                .developerLevel(DeveloperLevel.JUNIOR)
                .memberId("memberId2")
                .build();
        given(dMakerService.getAllEmployedDevelopers())
                .willReturn(Arrays.asList(seniorBEDeveloper, juniorFEDeveloper));
        //when
        //then
        mockMvc.perform(get("/developers").contentType(contentType))
                .andExpect(status().isOk())
                .andExpect(
                        jsonPath("$.[0].developerSkillType",
                                is(DeveloperSkillType.BACK_END.name())))
                .andExpect(
                        jsonPath("$.[0].developerLevel",
                                is(DeveloperLevel.SENIOR.name())))
                .andExpect(
                        jsonPath("$.[1].developerSkillType",
                                is(DeveloperSkillType.FRONT_END.name())))
                .andExpect(
                        jsonPath("$.[1].developerLevel",
                                is(DeveloperLevel.JUNIOR.name())));
    }
    @Test
    void testErrorMessage() throws Exception {
        //given
        given(dMakerService.getAllEmployedDevelopers())
                .willThrow(new DMakerException(DMakerErrorCode.NO_DEVELOPER));
        //when
        //then
        mockMvc.perform(get("/developers").contentType(contentType))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(
                        jsonPath("$.errorCode",
                                is(DMakerErrorCode.NO_DEVELOPER.name())));
    }
}
 
@WebMvcTest 장점
- WebApplication 관련된 Bean들만 등록하기 때문에 통합 테스트보다 빠르다.
- 통합 테스트를 진행하기 어려운 테스트를 진행 가능하다.
- ex) 결제 모듈 API를 콜하며 안되는 상황에서 Mock을 통해 가짜 객체를 만들어 테스트 가능.
 
 
 
 
@WebMvcTest 단점
- 요청부터 응답까지 모든 테스트를 Mock 기반으로 테스트하기 때문에 실제 환경에서는 제대로 동작하지 않을 수 있다.
 
 
 
 
@WebFluxTest
- Spring WebFluex component에 초점을 맞춰 Spring WebFlux test에 사용되는 annotation 이다.
- 전체 auto-configuration을 실행하지 않는 대신 WebFlux와 연관된 configuration만 실행한다.
- @Controller, @ControllerAdvice, @JsonComponent, Converter/GenericConverter, WebFluxConfigurer bean을 생성하고 적재한다.
- 하지만 @Component, @Service, @Repository bean은 생성되지 않는다.
 
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SampleControllerTest {
    @Autowired
    WebTestClient webTestClient;
    @MockBean 
    SampleService mockSampleService;
    @Test
    public void helloTest() throws Exception {
        when(mockSampleService.getName()).thenReturn("dave"); 
        webTestClient.get().uri("/hello").exchange()
                .expectStatus().isOk()  
                .expectBody(String.class).isEqualTo("hello dave");  
    }
}
- SpringMvcWebFlux에 추가된 RestClient 중 하나로 asynchronous하게 동작하여 request에 대한 response가 도착할 때 callback을 실행할 수 있습니다.
- 비동기 연결방식으로 동작하기 때문에 테스트 코드로 WebClient와 동일한 API를 사용할 수 있다.
- WebTestClient를 사용하려면 project에 spring-boot-starter-webflux 의존성을 추가해야 한다.
REFERENCES