我已经在 Kotlin 中使用 Spring 的 WebFlux 框架大约一个月了,并且一直很喜欢它。当我准备使用 WebFlux 和 Kotlin 编写生产代码时,我发现自己很难以简单、轻量级的方式对路由器进行单元测试。
Spring Test 是一个很好的框架,但它比我想要的更重,我正在寻找一个比传统 JUnit 更具表现力的测试框架。JavaScript的摩卡风格。Kotlin 的 Spek 完全符合要求。
以下是我如何使用Spek对简单路由器进行单元测试的示例。
WebFlux 使用 Kotlin 的类型安全构建器来构建路由器,定义了出色的 DSL。虽然语法非常简洁易读,但如何断言它返回的路由器函数 bean 配置正确并不明显,因为客户端代码大多无法访问其属性。
假设我们有以下路由器:
@Configuration
class PingRouter(private val pingHandler: PingHandler) {
@Bean
fun pingRoute() = router {
accept(MediaType.APPLICATION_JSON).nest {
GET("/ping", pingHandler::handlePing)
}
}
}
我们想断言,当一个请求与/ping
路由匹配时,application/json
内容标头,请求将传递给我们的处理程序函数。
object PingRouterTest: Spek({
describe("PingRouter") {
lateinit var pingHandler: PingHandler
lateinit var pingRouter: PingRouter
beforeGroup {
pingHandler = mock()
pingRouter = PingRouter(pingHandler)
}
on("Ping route") {
/*
We need to setup a dummy ServerRequest who's path will match the path of our router,
and who's headers will match the headers expected by our router.
*/
val request: ServerRequest = mock()
val headers: ServerRequest.Headers = mock()
When calling request.pathContainer() itReturns PathContainer.parsePath("/ping")
When calling request.method() itReturns HttpMethod.GET
When calling request.headers() itReturns headers
When calling headers.accept() itReturns listOf(MediaType.APPLICATION_JSON)
/*
We call pingRouter.pingRoute() which will return a RouterFunction. We then call route()
on the RouterFunction to actually send our dummy request to the router. WebFlux returns
a Mono that wraps the reference to our PingHandler class's handler function in a
HandlerFunction instance if the request matches our router, if it does not, WebFlux will
return an empty Mono. Finally we invoke handle() on the HandlerFunction to actually call
our handler function in our PingHandler class.
*/
pingRouter.pingRoute().route(request).subscribe({ it.handle(request) })
/*
If our pingHandler.handlePing() was invoked by the HandlerFunction, we know we properly
configured our route for the request.
*/
it("Should call the handler with request") {
verify(pingHandler, times(1)).handlePing(request)
}
}
}
})