2020. 3. 20. 21:44ㆍWeb/Spring
이전 시간에 DB관련 테스트 작성하는 법을 다루어 봤는데, 이번 시간에는 Webflux handler 테스트 코드를 한번 작성해보려고 한다. 이전까지는 service 단까지만 테스트를 모듈별로 작성하였지만, 핸들러로 인입하여 한번에 모든 로직을 돌려보는 테스트는 직접 넣어보지 않았던 것 같다.(사실 핸들러, 컨트롤러 테스트가 이것저것 설정해야할 것들이 많아서..)
간단히 바로 예제를 다루어본다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
@Configuration
public class RouteConfig {
@Bean
public RouterFunction<ServerResponse> routeFunction(TestHandler testHandler) {
return route()
.nest(path("/"), builder -> builder
.GET("/test", accept(APPLICATION_JSON), testHandler::testHandler)
).build();
}
}
@Component
public class TestHandler {
@Autowired
private TestService testService;
public Mono<ServerResponse> testHandler(ServerRequest serverRequest) {
return ServerResponse.ok().body(BodyInserters.fromProducer(testService.testService(), String.class));
}
}
@Service
public class TestService {
public Mono<String> testService() {
return Mono.just("testResponse");
}
}
|
cs |
간단하게 handler와 service 클래스를 구현하였다. 단순히 String을 반환하고 있다. 해당 핸들러를 테스트하는 코드는 아래와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
@SpringBootTest(classes = {
TestHandler.class,
RouteConfig.class,
TestService.class
})
@MockBeans({
})
class HandlerTest {
private WebTestClient client;
@Autowired
private RouteConfig routeConfig;
@Autowired
private TestHandler testHandler;
private static final String ENDPOINT = "/test";
@BeforeEach
public void beforeTest() {
client = WebTestClient
.bindToRouterFunction(routeConfig.routeFunction(testHandler))
.build();
}
@Test
public void handlerTest() {
client.get()
.uri(ENDPOINT)
.exchange()
.expectStatus().isOk()
.expectBody(String.class)
.value(value -> {
assertEquals(value, "testResponse");
});
}
}
|
cs |
spring MVC와는 조금 다를 수 있다. 해당 테스트 코드를 간단하게 설명하면 우리가 작성한 routeConfig를 WebTestClient에 바인딩해준다. 그 이후에 해당 클라이언트를 이용하여 routeConfig 내에 존재하는 라우팅 엔드포인트에 요청을 날리면 핸들러 로직을 수행후 응답을 내려준다. 그리고 해당 응답을 이용하여 원하는 응답이 나왔는지 assert 해볼 수 있다. 위는 정상적인 경우만 다루어보았고, 한번 없는 요청을 보내보자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
@SpringBootTest(classes = {
TestHandler.class,
RouteConfig.class,
TestService.class
})
@MockBeans({
})
class HandlerTest {
private WebTestClient client;
@Autowired
private RouteConfig routeConfig;
@Autowired
private TestHandler testHandler;
private static final String ENDPOINT = "/test";
private static final String INVALID_ENDPOINT = "/invalid";
@BeforeEach
public void beforeTest() {
client = WebTestClient
.bindToRouterFunction(routeConfig.routeFunction(testHandler))
.build();
}
@Test
public void handlerTest() {
client.get()
.uri(ENDPOINT)
.exchange()
.expectStatus().isOk()
.expectBody(String.class)
.value(value -> {
assertEquals(value, "testResponse");
});
}
@Test
public void notFoundHandlerMappingTest() {
client.get()
.uri(INVALID_ENDPOINT)
.exchange()
.expectStatus().is4xxClientError();
}
}
|
cs |
테스트를 하나더 추가하였다. 이 요청 url은 존재하지 않는 handler mapping이므로 정상적이라면 404 not found를 응답코드로 내려줄 것이라고 기대하고 테스트코드를 작성하였다.
실행해보면 테스트가 정상적으로 통과한다.
여기까지 간단하게 웹플럭스 핸들러 테스트를 작성해보았다. 아주 간단하게만 작성하였지만 테스트에 고려할만한 TC(Test Case)는 아주 많은 것같다. 400 예외를 고려한 테스트, 혹은 특정 예외를 고려한 테스트 혹은 모든 로직이 잘 동작하는 테스트 마지막으로 파라미터마다 동작이 다른것을 테스트 해보는 경우등 아주 많은 경우가 있을 것이다. 모든 테스트케이스를 잘 고려해 테스트를 짜보자.
마지막으로 만약 애플케이션에 ErrorHandler가 적용되어있다면 위와 같이 짠 테스트 코드는 ErrorHandler가 내뱉는 응답을 받지 못한다. 왜냐 테스트 컨텍스트 빈으로 등록하지 않았기 때문이다. 만약 파라미터가 잘못들어와 로직상에서 예외를 던졌고, 만약 그 예외를 ErrorHandler가 받아서 400이라는 HttpStatusCode로 리턴하고 있는데 그것을 기대하고 파라미터가 잘못들어온 경우를 is4xxClientError()로 잡으면 테스트는 통과하지 못한다. 그 이유는 WebTestClient는 500에러를 내뿜고 있기 때문이다. 그 이유는 위에 말한 것과 같이 ErrorHandler를 테스트 컨텍스트 빈으로 등록하지 않아서 4xx로 변환되지 못하고 애플리케이션 로직상에서 나는 예외(500)로 인식하기 때문이다.