Quantcast
Channel: all and sundry
Viewing all articles
Browse latest Browse all 250

Annotated controllers - Spring Web/Webflux and Testing

$
0
0
Spring Webflux and Spring Web are two entirely different web stacks. Spring Webflux, however, continues to support an annotation-based programming model

An endpoint defined using these two stacks may look similar but the way to test such an endpoint is fairly different and a user writing such an endpoint has to be aware of which stack is active and formulate the test accordingly.

Sample Endpoint

Consider a sample annotation based endpoint:


import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController


data class Greeting(val message: String)

@RestController
@RequestMapping("/web")
class GreetingController {

@PostMapping("/greet")
fun handleGreeting(@RequestBody greeting: Greeting): Greeting {
return Greeting("Thanks: ${greeting.message}")
}

}


Testing with Spring Web

If Spring Boot 2 starters were used to create this application with Spring Web as the starter, specified using a Gradle build file the following way:

compile('org.springframework.boot:spring-boot-starter-web')

then the test of such an endpoint would be using a Mock web runtime, referred to as Mock MVC:

import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.test.context.junit4.SpringRunner
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content


@RunWith(SpringRunner::class)
@WebMvcTest(GreetingController::class)
class GreetingControllerMockMvcTest {

@Autowired
lateinit var mockMvc: MockMvc

@Test
fun testHandleGreetings() {
mockMvc
.perform(
post("/web/greet")
.content("""
|{
|"message": "Hello Web"
|}
""".trimMargin())
).andExpect(content().json("""
|{
|"message": "Thanks: Hello Web"
|}
""".trimMargin()))
}
}


Testing with Spring Web-Flux

If on the other hand Spring-Webflux starters were pulled in, say with the following Gradle dependency:

compile('org.springframework.boot:spring-boot-starter-webflux')

then the test of this endpoint would be using the excellent WebTestClient class, along these lines:

import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest
import org.springframework.http.HttpHeaders
import org.springframework.test.context.junit4.SpringRunner
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.web.reactive.function.BodyInserters


@RunWith(SpringRunner::class)
@WebFluxTest(GreetingController::class)
class GreetingControllerTest {

@Autowired
lateinit var webTestClient: WebTestClient

@Test
fun testHandleGreetings() {
webTestClient.post()
.uri("/web/greet")
.header(HttpHeaders.CONTENT_TYPE, "application/json")
.body(BodyInserters
.fromObject("""
|{
| "message": "Hello Web"
|}
""".trimMargin()))
.exchange()
.expectStatus().isOk
.expectBody()
.json("""
|{
| "message": "Thanks: Hello Web"
|}
""".trimMargin())
}
}


Conclusion

It is easy to assume that since the programming model looks very similar using Spring Web and Spring Webflux stacks, that the tests for such a legacy test using Spring Web would continue over to Spring Webflux, this is however not true, as a developer we have to be mindful of the underlying stack that comes into play and formulate the test accordingly. I hope this post clarifies how such a test should be crafted.

Viewing all articles
Browse latest Browse all 250

Trending Articles