2

I am writing a simple HTTP server with Spring WebFlux using the Functional API in Kotlin.

As the documentation says, a HandlerFuction handles incoming ServerRequests by returning a Mono<ServerResponse> on invocation.

Let's assume, we have the following function which responses with either OK or BAD_REQUEST depending on whether the name path variable is presented:

val helloHandler = HandlerFunction { req ->
    try {
        val name = req.pathVariable("name")
        ok().bodyValue("Hello $name!")
    } catch (_: IllegalArgumentException) {
        badRequest().build()
    }
}

The Testing section of Spring Reference Guide implies that this function should be tested with the conjunction of its RouterFunction using WebTestClient. Such a test would look like this:

val helloHandlerRouter = RouterFunctions.route()
    .GET("/hello/{name}", helloHandler)
    .build()

val client = WebTestClient
    .bindToRouterFunction(helloHandlerRouter)
    .build()
    
client.get().uri("/hello/Panda").exchange()
    .expectStatus().isOk
    .expectBody<String>().isEqualTo("Hello Panda!")

While this test seems satisfying, it highly depends on the underlying routing mechanism. For example, it would return NOT_FOUND instead of BAD_REQUEST on missing name path variable ignoring the actual handler logic. The router intercepts by simply not forwarding the request to the handler, ergo, the following test is testing the router not the handler:

client.get().uri("/hello").exchange()
    .expectStatus().isBadRequest // FAILS: NOT_FOUND != BAD_REQUEST 
    .expectBody().isEmpty

How to test the HandlerFunction without its router?

I would like to test the HandlerFunction in isolation, somehow like this:

val request = MockServerRequest.builder()
    .pathVariable("name", "Panda")
    .build()
val response: Mono<ServerResponse> = helloHandler.handle(request).block()

// ??? verify ???

But I did not find a sufficient way to watch inside to the response and verify its values.

Could you please advise on how to test HandleFunctions separately from their RouterFunctions?

zokni
  • 41
  • 5
  • show us the part where the handler function is mapped to its url. – Toerktumlare Oct 15 '20 at 10:22
  • Thank you, @ThomasAndolf. Find the `RouterFunction` implementation on the description now. – zokni Oct 15 '20 at 10:41
  • the problem is that you assume that `/hello` and `/hello/{name}` is the url which it is not. These are 2 separate endpoints. – Toerktumlare Oct 15 '20 at 12:18
  • Or even more accurately the `RouterFunction` will return an empty `Mono` since no matching `RequestPredicate` found on _GET /hello_. Therefore, the router is responding with _404_ (see [here](https://github.com/spring-projects/spring-framework/blob/5.2.x/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java#L1005)). But this behaviour is still in the router, while my intention is testing the `HandlerFunction`. – zokni Oct 15 '20 at 13:56
  • if you wish to test your handler function you should use a step verifier, and then mock your request and not use a mock webclient – Toerktumlare Oct 15 '20 at 16:53
  • I agree with you @ThomasAndolf. As you can see in the description, I also tried to avoid the use of `WebTestClient` and invoke the handler function directly. I also believe that in this case, it does not matter whether the test is flattened out by a `Mono::block()` call or it utilises a `StepVerifier`. Anyhow, could you please provide a working example? – zokni Oct 15 '20 at 17:54
  • In this particular case, block and stepverifier acts the same, but you should not make it a habit of using block since blocking is bad in any async application. And no i wont provide a working example, there is plenty of documentation out there explaining the usage of step verifiers, and how to mock classes. And you say you want to test your handler, thats incorrect, instead decied WHAT it is with your handler that you want to test. Is status code? body? return values? – Toerktumlare Oct 15 '20 at 18:27

0 Answers0