테스트 코드 관련

MINJU·2024년 4월 22일
0

내가 직접 손으로 타이핑 안해보면 집중이 안돼서 쓰는 이것저것 모음집 ^ ^ . .

Mocking이란?

참조
테스트를 위해 실제 객체를 사용하는 것처럼 테스트를 위해 만든 모형!!

Spring Boot 통합테스트, 단위테스트

참조 블로그

(1) 통합테스트

  • 실제 운영 환경에서 사용될 클래스들을 통합해서 테스트
  • 스프링 프레임워크에서 전체적으로 플로우가 제대로 동작하는지 검증하는 것
  • 어플리케이션의 설정과 모든 을 로드하기 때문에 운영 환경과 가장 유사한 테스트가 가능하지만, 시간이 오래 걸리고 무겁다

@SpringBootTest

  • 스프링부트는 위의 어노테이션을 통해 스프링부트 어플리케이션 테스트에 필요한 거의 모든 의존성을 제공한다.
  • 해당 어노테이션을 선언하면 @SpringBootApplication을 찾아서 그 기준으로 bean들을 등록시켜준다. 그렇기 때문에 자동으로 bean 주입이 가능해지고, 주입되는 빈들을 mocking할 수도 있다.

@SpringBootTest 옵션

<1> properties
// 프로퍼티를 key=value 형태로 직접 추가 가능
@SpringBootTest(properties = {"name=minju"}
public class ExSpringBootTest {
	@Value("${name}")
    private STring name;
    ...
   }
}
// src/test/test.yml에 값을 정의해놓고 긁어올 수도 있음
@SpringBootTest(properties = {"spring.config.location = classpath:test.yml"})
public class ExSpringBootTest {
	@Value("{minju.age}")
    private int age;
    ...
    }
}
<2> webEnvironment
  • 웹 테스트 환경 구성이 가능한 옵션
  • 종류는 아래와 같다
    - Mock
    • RANDOM_PORT
    • DEFINED_PORT
    • NONE

참조 블로그

-1- Mock
  • 실제 객체를 만들기엔 비용이 많이 들거나, 의존성 때문에 구현이 어려울 경우 가짜 객체를 만들어 사용하는데, 이때 사용한다.
  • WebApplicationContext를 불러오며 내장된 서블릿 컨테이너가 아닌 mock 서블릿을 제공한다.
  • @SpringBootTest(webEnvironment=...) 옵션의 기본값!
  • @AutoConfigureMockMvc 어노테이션을 함께 사용하여 MockMvc를 사용한 테스트 진행 가능
    -> MockMvc는 브라우저에서 요청과 응답을 의미하는 객체. Controller 테스트 사용을 용이하게 해주는 라이브러리
    -> @AutoConfigureMockMvc 어노테이션은 Mock 테스트시 필요한 의존성을 제공한다.
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
@AutoConfigureMockMvc
public class ExControllerTest_Mock {
	
    @Autowired
    MockMvc mockMvc;
    
    @Test
    public void exTest() throws Excpetion {
    	mockMvc.performt(get("/"))
        		.andExpect(status().isOk())
                .andExpect(content().string("Hello"));
    }
}
  • webEnvironment를 mock으로 설정하게 되면 -> servlet container를 실제로 띄우는 것이 아니라 Mocking하여 띄운다
  • 그래서 mocking된 servlet container와 인터랙션을 하기 위해선 MockMvc 클라이언트를 사용해야 한다.
  • 이때 @AutoConfigureMockMvc를 선언하면 MockMvc를 주입받을 수 있다.
-2- RANDOM_PORT + RestTemplate
  • 내장된 WebApplicationContext를 불러오며 실제 서블릿 환경을 구성함
  • 임의의 포트를 지정함
  • 실제 서블릿 컨테이너를 사용하기 때문에 TestRestTemplate 사용
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class exTestController_Random_port  {
	@Autowired
    TestRestTemplate testRestTemplate;
    
    @MockBean
    ExService exService;
    
    @Test
    public void exTest() throws Exception {
    	when(exService.getName()).thenReturn("~~");
        
        String result = testResetTemplate.getForObjcet("/", String.class);
        Assertions.assertThat(result).isEqualTo("hello");
        }
}
  • 실제로 내장 톰캣이 뜬다.
  • 그래서 MockMvc가 아니라 RestTemplate을 사용하여 내장톰캣과 interaction이 가능하다.
  • 만약 해당 클래스의 목적이 컨트롤러로 요청과 응답을 보기 위함이라면 @MockBean을 사용해서 해당 컨트롤러에서 사용하는 빈을 mocking할 수 있다.
-3- RANDOM_PORT + WebTestClient
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class test_WebTestCleint {
	
    @Autowired
    WebTestClient webTestClient;
    
    @MockBean
    TestMockService testMockService;
    
    @Test
    public void testMethod() throws Exception {
    	when(testMockService.getName()).thenReturn("~~");
        
        webClientTest.get().uri("/").exchange()
        .expectStatus().isOk()
        .expectBody(String.class).isEqualTo("hello");
    }
}
  • WebTestClient는 자바 5 spring webflux에 추가된 RestClient 중 하나
  • Async!!여서 sync인 RestClient보다 성능적으로 더 좋다고 볼 수 있다.

WebTestClient와 RestTemplate 비교

  • WebTestClient는 spring5부터 지원한다.
  • RestTemplate은 3부터 지원한다.
  • WebTestClient는 NonBlock + Async
  • RestTemplate은 Sync
-4- DEFINED_PORT
  • RANDOM_PORT와 동일하게 실제 서블릿 환경을 구성하지만, 포트는 어플리케이션 프로퍼티에서 지정한 포트를 지정한다.
  • 실제 서블릿 컨테이너를 사용하므로 !! TestRestTemplate 사용한다.

@MockBean

  • mock 객체를 빈으로서 등록할 수 있다.
  • @MockBean 적용시 스프링의 ApplicationContext는 Mock 객체를 빈으로 등록하며, @MockBean으로 선언된 객체와 같은 이름과 타입으로 이미 빈이 등록되어 있다면 해당 빈은 선언한 @MockBean으로 대체된다.

(2) 단위 테스트

종류는!

  • @JsonTest
  • @WebMvcTest
  • @WebFluxTest
  • @DataJpaTest
  • @RestClientTest

<1> @JsonTest

  • JSON 직렬화를 테스트하는데 필요한 스프링 빈만을 사용하여 Spring TestContext를 자동으로 구성할 수 있다.

예제

직렬화/역직렬화 대상이 되는 클래스 정의

@JsonNaming(PropertyNamingStrategies.LowerCaseStrategy.class)
@Getter
@Setter
@AllArgsConstructor
public class UserDetails {
	private Long id;
    private String firstName;
    private String lastName;
    
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy.MM.dd")
    private LocalDate dateOfBirth;
    
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private boolean enabled;
}

@JsonTest를 사용하여 직렬화, 역직렬화와 관련된 테스트 코드 추가

@JsonTest
public class SampleControllerJsonTest {

	@Autowired
    private JacksonTester<UserDetails> json;
    
    @Test
    public void testSerialize() throws IOException {
    	UserDetails userDetails = new UserDetails(1L, "Duke", "Java", LocalDate.of(1999.3.8), true);
    
    JsonContent<UserDetails> result = this.json.write(userDetails);
    
    assertThat(result).hasJsonPathStringValue("$.firstname");
    
    assertThat(result).extractingJsonPathStringValue("$.firstname).isEqualTo("Duke"0;
    
    assertThat(result).extractingJsonPathStringValue("$.lastname").isEqualTo("Java");
    
    assertThat(result).extractingJsonPathStringValue("$.dateofbirth").isEqualTo("1999.03.08"0;
    }
    
    
    @Test
    public void testDeserialize() throws IOException {
    	String jsonContent = "{\\"firstname\\":\\"Mike\\", \\"lastname\\": \\"Meyer\\"," +
                " \\"dateofbirth\\":\\"1990.05.15\\"," +
                " \\"id\\": 42, \\"enabled\\": true}";
        
        UserDetails result = this.json.parse(jsonContent).getObject();
        
        assertThat(result.getFirstName()).isEqualTo("Mike");
        ...
        assertThat(result.getDateOfBirth()).isEqualTo(LocalDate.of(1990, 05,15));
        }
}

<2> @WebMvcTest

  • 웹 상에서 요청과 응답에 대한 테스트가 가능함
  • @WebMvcTest를 사용하면 @Controller, @ControllerAdvice, @JsonComponent, @JsonFilter, WebMvcConfigure, HandlerMethodArgumentResolver만 로드되기 때문에 전체 테스트보다 가볍다

예제

@WebMvcTest(SampleController.class) // 컨트롤러만 빈으로 등록함
public class SampleControllerWebMvcTest {

	@MockBean
    SampleService mockSampleService; // 필요한 빈은 mock bean으로
    
    @Autowired
    MockMvc mockMvc;
    
    @Test
    public void testHell() throws Exception {
    	when(mockSampleservice.getName()).thenReturn("minju");
        
        mockMvc.perform(get("/hello"))
        	.andExpect(status().isOk())
            .andExpect(content().string("hello minjU"))
            .andDo(print());
        }
}

<3> @WebFluxTest

Spring WebFlux : 스프링 5에서 새로 등장한 웹 어플리케이션에서 리액티브 프로그래밍을 제공하는 프레임워크

  • SpringWebFlux 컨트롤러가 예상대로 작동하는지 테스트하려면 위의 어노테이션을 사용할 수 있다!
  • @WebFluxTest는 단일 컨트롤러로 제한할 수 있으며 @MockBean 애노테이션과 함께 사용하여 비용이 큰 객체를 mock 객체로 대체할 수 있음
  • @WebFluxTest는 전체 http 서버를 시작하지 않고도, webflux 컨트롤러를 신속하게 테스트할 수 있는 방법을 제공하는 webTestClient를 자동 구성함

예제

@WebFluxTest(SampleController.class)
public class SampleControllerWebFluxTest {
	@MockBean
    SampleService mockSampleService;
    
    @Autowired
    WebTestClient webTestClient;
    
    @Test
    public void testHeelo() {
    	when(mockSampleService.getName()).thenReturn("minjU");
        
        webTestClient.get().uri("/hello")
        	.exchange()
            .expectStatus().isOk()
            .expectBody(String.class).isEqualTo("hello minjU");
      }
}

@DataJpaTest

  • JPA와 관련된 설정만 불러옴
  • @Entity 클래스를 스캔하여 스프링 데이터 jpa 저장소를 구성
  • 기본적으로 인메모리 db를 이용
  • 데이터 소스의 설정이 정상적인지, jpa를 사용해서 데이터를 제대로 생성, 수정, 삭제하는지 등의 테스트가 가능함
  • 테스트가 끝날 때마다 자동으로 테스트에 사용한 데이터를 롤백함

@RestClientTst

  • REST 통신의 JSON 형식이 예상대로 응답을 반환하는지 등을 테스트함

0개의 댓글

관련 채용 정보