스프링부트와 리액터에서는 비동기, 논블로킹 코드도 JUnit을 이용해 테스트할 수 있다.
리액티브 단위 테스트
리액티브 코드를 테스트할 때 핵심은 기능만을 검사하는 게 아니라 리액티브 스트림 시그널도 함께 검사해야 한다는 점이다. 리액티브 스트림은 onSubscribe, onNext, onError, onComplete를 말한다.
@ExtendWith(SpringExtension.class)
class InventoryServiceTest {
InventoryService inventoryService;
@MockBean
private ItemRepository itemRepository;
@MockBean
private CartRepository cartRepository;
@BeforeEach
void setUp() {
Item sampleItem = new Item("item1", "TV tray", "Alf TV tray", 19.99);
CartItem sampleCartItem = new CartItem(sampleItem);
Cart sampleCart = new Cart("My Cart", Collections.singletonList(sampleCartItem));
when(cartRepository.findById(anyString())).thenReturn(Mono.empty());
when(itemRepository.findById(anyString())).thenReturn(Mono.just(sampleItem));
when(cartRepository.save(any(Cart.class))).thenReturn(Mono.just(sampleCart));
inventoryService = new InventoryService(itemRepository, cartRepository);
}
}
기본적인 테스트 셋업은 위와 같이 한다. 단위 테스트를 수행할 것이므로 MockBean을 주입하여 테스트하자.
@Test
void addItemToEmptyCartShouldProduceOneCartItem(){
inventoryService.addItemToCart("My Cart", "item1")
.as(StepVerifier::create)
.expectNextMatches(cart -> {
assertEquals(cart.getCartItems().stream().map(CartItem::getQuantity), 1);
assertEquals(cart.getCartItems().stream().map(CartItem::getItem),
new Item("item1","TV tray","Alf TV tray", 19.99));
return true;
});
}
테스트 코드도 누군가가 구독을 해야 무슨 일이 발생하고 검증도 할 수 있게 된다. StepVerifier라는 클래스?가 구독을 한다. 결괏값을 얻기 위해 블로킹 방식으로 기다리는 대신에 리액터의 테스트 도구가 대신 구독을 하고 값을 확인할 수 있게 해준다. 값을 검증할 수 있는 적절한 함수를 expectNextMatches에 람다식 인자로 전달해주고, verifyComplete()를 호출해 onComplete 시그널을 확인하면 의도한 대로 테스트가 동작했음이 보장된다.
내장 컨테이너 테스트 실행
종단 간 테스트는 비용이 많이 들지만 넓은 범위의 테스트를 할 필요가 있다. 스프링 부트의 강력한 기능인 내장 웹 컨테이너를 사용해 어렵지 않게 테스트 코드를 구현할 수 있다.
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) // 1
@AutoConfigureWebTestClient // 2
public class LoadingWebSiteIntegrationTest {
@Autowired
WebTestClient client;
@Test
void test() { // 3
client.get().uri("/").exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.TEXT_HTML)
.expectBody(String.class)
.consumeWith(exchangeResult -> {
assertThat(exchangeResult.getResponseBody()).contains("<a href=\"/add");
});
}
}
- 스프링 부트가 실제 애플리케이션을 구동하게 만든다. @SpringBootApplication이 붙은 클래스를 찾아서 내장 컨테이너를 실행한다. WebEnvironment.RANDOM_PORT는 테스트할 때 임의의 포트에 내장 컨테이너를 바인딩한다.
- 애플리케이션에 요청을 날리는 WebTestClient 인스턴스를 생성한다.
- 실제 테스트 메소드는 WebTestClient를 사용해서 홈 컨트롤러의 루트 경로를 호출한다.
'Develop > Spring˙Spring Boot' 카테고리의 다른 글
[Spring Reactive] RabbitMQ를 이용해 메시지 브로커 사용하기 (0) | 2023.03.21 |
---|---|
[Spring Boot] 스프링 부트의 메시징 솔루션 (0) | 2023.03.16 |
[Spring Reactive] 리액티브 데이터 Repository 정의하기 (0) | 2023.03.12 |
[Spring Reactive] 리액티브 데이터베이스 저장 요건 (0) | 2023.03.10 |
[Spring Reactive] WebFlux를 사용해 Reactive Spring Boot 구성해보기 (0) | 2023.03.08 |