ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 테스트
    백기선(인프런 강의)/스프링 부트 개념과 활용 2020. 4. 16. 23:43
    반응형

    테스트란

    테스트 파일에는 @SpringBootTest가 아래와같이 붙어있다. @SpringBootTest 어노테이션은 @SpringBootApplication 어노테이션이 붙어있는 스프링 메인 애플리케이션을 찾아간다. 이후 메인부터 시작하는 모든 Bean을 찾는다. 다음으로는 테스트용 애플리케이션 context를 만들면서 Bean을 등록해준다. 이 중에 MockBean에 해당되는 Bean을 찾아서 교체를 해준다. 교체된 MockBean은 테스트마다 리셋이된다.

     

     

    테스트 예시 코드

    @RestController
    public class SampleController {
    	
    	@Autowired
    	private SampleService sampleService;
    	
    	@GetMapping("hello")
    	public String hello() {
    		return "hello " + sampleService.getName();
    	}
    
    }

     

    @Service
    public class SampleService {
    
    	public String getName() {
    		return "JinSeok";
    	}
    }

     

    위와같이 테스트를 하기위해서는 pom.xml에 아래와 같이 의존성이 정의되어야한다.

     

    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-test</artifactId>
    	<scope>test</scope>
    </dependency>

     

     

    Mock 타입 테스트

    Mock타입은 서블릿 컨테이너(톰캣 같은 것)를 테스트용으로 띄우지 않고, Mockup을 해서 서블릿을 Mocking 한 것을 띄워준다. dispatcherServlet이 만들어지긴 하는데 Mockup이 되어, dispatcherServlet에 요청을 보내는 것 처럼 실험을 할 수 있다. 이 때 mockup 된 서블릿과 상호작용을 하려면 MockMVC라는 클라이언트를 사용해야 한다.

    MockMVC 라는 클라이언트를 사용하려면 @AutoConfigureMockMvc 어노테이션을 붙여주고, 주입 사용하면 된다.

    Mock 객체는 내장 톰캣이 구동이 안된다.

     

    코드설명

    • mockMvc.perform(get("/hello"))
      • get요청을 보내는 것
    • andExpect()
      • ~하길 기대하는 것
    • andDO();
      • ~을 실행
    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
    @AutoConfigureMockMvc
    public class SampleControllerTest {
    	@Autowired
    	MockMvc mockMvc;
    	
    	@Test
    	public void hello() throws Exception {
    		mockMvc.perform(get("/hello"))
    		.andExpect(status().isOk())
    		.andExpect((ResultMatcher) content().string("hello JinSeok"))
    		.andDo(print());
    	}
    }

     

    결과

     

     

    RANDOM-PORT / DEFINED-PORT 타입

    실제로 서블릿 컨테이너(was 종류 - 내장 톰캣)이 뜬다. 테스트용 rest template나, 테스트용 웹 클라이언트를 사용해야 된다.

     

    RANDOMRANDOM_PORT는 랜덤한 포트를 배정받고, DEFINED_PORT는 포트를 정해줄 수 있다.

     

     

    TestRestTemplate

    TestRestTemplate을 주입받아서 사용을 한다.

    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
    @AutoConfigureMockMvc
    public class SampleControllerTest {
    	
    	@Autowired
    	TestRestTemplate testRestTemplate;
    	
    	@Test
    	public void hello() throws Exception {
    		String result = testRestTemplate.getForObject("/hello", String.class);
    		assertThat(result).isEqualTo("hello Jinseok");
    	}
    }

     

    @MockBean

    SampleService까지 말고 SampleController까지만 가게 하고 싶을 때는 @MockBean을 사용하면 된다.  이 어노테이션을 사용하여 Service를 만들어 컨트롤 할 수 있다.

     

    application context안에 들어있는 SampleService Bean@MockBean으로 교체해주기 때문에 mockSampleService를 사용하게 된다.

     

    SampleService에서 getNameJinseok을 반환하지만 mockSampleServicejs라는 문자열을 리턴한다. 

     

    이 테스트에서는 mockSampleService를 사용하기 때문에 junit5 테스트를 하게 된다면 hello JinSeok이 아닌 hello js가 나오게 된다.

     

    SampleController가 사용하는 SampleServicemocking해서 BeanmockSampleService로 교체를 하였다

     

    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    @AutoConfigureMockMvc
    public class SampleControllerTest {
    	
    	@Autowired
    	TestRestTemplate testRestTemplate;
    	
    	@MockBean
    	SampleService mockSampleService;
    	
    	@Test
    	public void hello() throws Exception {
    		when(mockSampleService.getName()).thenReturn("js");
    		String result = testRestTemplate.getForObject("/hello", String.class);
    		assertThat(result).isEqualTo("hello js");
    	}
    }

     

    WebTestClient

    WebClient는 Rest Client중에 하나이다. 

     

    기존에 사용하는 Rest Client는 synchronous(동기)이다. 하지만 WebClient는 asynchronous(비동기)로 되어있다. 요청을 보내고, 기다리지 않고, 응답이 오면 call back이 온다. 이때 call back을 실행을 할 수 있다.

     

    WebTestClient는 테스트할 때 WebClient와 비슷한 API를 사용할 수 있다.

     

    WebClient를 사용할라면 아래와같이 의존성이 추가되어야 한다.

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-webflux</artifactId>
      </dependency>

     

    status와 body를 체크해 준다.

    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    @AutoConfigureMockMvc
    public class SampleControllerTest {
    	
    
    	@Autowired
    	WebTestClient webTestClient;
    	
    	@MockBean
    	SampleService mockSampleService;
    	
    	@Test
    	public void hello() throws Exception {
    		when(mockSampleService.getName()).thenReturn("js");
    		
    		webTestClient.get().uri("/hello").exchange().expectStatus().isOk()
    				.expectStatus().isOk()
    				.expectBody(String.class).isEqualTo("hello JinSeok");
    	}
    }

     

     

     

    슬라이스 테스트

    @SpringBootTest 이 어노테이션이 @SpringBootApplication을 찾아간다. 여기서부터 시작하는 Bean을 다 찾고, SpringBootTest에 저장을 한다. Bean들을 찾아 Mock Bean과 교체해 준다. 즉 모든 Bean을 교체 및 사용한다.

     

    위와 다르게 테스트할 Bean만 등록하고 싶을 때 사용하는 것이 슬라이스 테스트이다.

     

    웹 레이어(@Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, Filter, WebMvcConfigurer, HandlerMethodArgumentResolver)만 Bean으로 등록해준다는 특징이 있다. @Service, @Repository 같은 Bean들은 Bean으로 등록해주지 않는다.

     

    그렇기 때문에 테스트 시 의존성이 깨지기 때문에 @MockBean으로 받아서 사용한다.

     

    @RunWith(SpringRunner.class)
    @WebMvcTest(SampleController.class)
    public class SampleControllerTest {
    
    	@Autowired
    	MockMvc mocMvc;
    
    	@MockBean
    	SampleService mockSampleService;
    
    	@Test
    	public void hello() throws Exception {
    		when(mockSampleService.getName()).thenReturn("js");
    		mocMvc.perform(get("/hello"))
    				.andExpect(content().string("hello js"));
    	}
    }

     

    위와같이 사용하면 SampleController만 Bean으로 등록되어 가벼운 테스트가 된다.

     

    @WebMvcTest 말고도, @JsonTest, @WebFluxTest, @DataJpaTest 등도 슬라이스 테스트 방법이다.

     

     

     

    OutputCapture

    junit의 Rule을 확장해서 만든 것이다.

     

    로그를 비롯한 console에 찍히는 모든 것을 캡쳐 한다.

     

    @RestController
    public class SampleController {
    	Logger logger = LoggerFactory.getLogger(SampleController.class);
    	@Autowired
    	private SampleService sampleService;
    	
    	@GetMapping("/hello")
    	public String hello() {
    		logger.info("여기는 컨트롤러");
    		System.out.println("skip");
    		return "hello " + sampleService.getName();
    	}
    }

     

    테스트 클래스에서 OutputCapture Rule을 선언해서(꼭 public으로 해야한다.) 테스트 해볼 수 있다.

    @RunWith(SpringRunner.class)
    @WebMvcTest(SampleController.class)
    public class SampleControllerTest {
    
    	@Rule
    	public OutputCaptureRule outputcapture = new OutputCaptureRule();
    	
    	@Autowired
    	MockMvc mocMvc;
    
    	@MockBean
    	SampleService mockSampleService;
    
    	@Test
    	public void hello() throws Exception {
    		when(mockSampleService.getName()).thenReturn("js");
    		mocMvc.perform(get("/hello"))
    				.andExpect(content().string("hello js"));
    		
    		assertThat(outputcapture.toString())
    			.contains("여기는 컨트롤러")
    			.contains("skip");
    	 
    	}
    }
    반응형

    '백기선(인프런 강의) > 스프링 부트 개념과 활용' 카테고리의 다른 글

    Spring Web MVC -HttpMessageConverters  (0) 2020.04.22
    Spring-Boot-Devtools  (0) 2020.04.21
    스프링 부트 기본 로거 설정  (0) 2020.04.16
    프로파일  (0) 2020.04.14
    외부 설정 2부  (0) 2020.04.13
Designed by Tistory.