对 Rest API 客户端进行单元测试的方法是什么 - 即测试是否发送了正确的 Rest 请求



在工作中,我们正在开发一种介于其他两个服务之间的服务。我们公开了一个由某个服务调用的 Rest API——请求由某个逻辑处理,然后根据逻辑,HTTP 请求将被发送到另一个服务。

互联网上有很多关于如何最好地测试你提供的 API 端点的资源。另一方面,我想测试发送了哪些 API 请求,而不是将它们发送到实际服务。

我想我总是可以在 localhost:8080 上设置一个完整的服务器框架,只记录它收到的内容,但这感觉有点脏。测试我们为外部服务提供的 Rest API(我们使用 akka-http 来做到这一点(使用 akka-http-testkit 进行测试非常舒服,这非常出色。我只是想知道是否有任何相对舒适的工具来测试 Http 请求。

函数式编程"工具">

我发现测试这些场景的最简单方法是在设计中使用普通的函数式编程原则。 您可以将Route创建嵌入到高阶函数中。 这个高阶函数将接受查询下游服务的函数:

type ComputedData = ???
val computeData : HttpRequest => ComputedData = ???
def intermediateRoute(downstreamService : ComputedData => Future[HttpResponse]) : Route = 
extractRequest { request =>
val computedData : ComputedData = computeData(request)
complete(downstreamService(computedData))
}

此高阶函数现在可以在生产中使用:

val queryDownStreamService : ComputedData => Future[HttpResponse] = ???
val productionRoute : Route = intermediateRoute(queryDownStreamService)

或者,它可以在单元测试中使用,以确保逻辑正确:

val testComputedData : ComputedData => Boolean = ???
val testResponse : HttpResponse = ???
val testService : ComputedData => Future[HttpResponse] = (computedData) => {
assert(testComputedData(computedData))
Success(testResponse)
}
val testRoute = intermediateRoute(testService)
Get("/test") ~> testRoute ~> check {
response should be testResponse
}

我们按照你所说的方式做dirty,尽管我认为这不是dirty

我们有一个启动/关闭服务器的基本特征(我们使用 HTTP4s 和 Scalatest(

trait EmbeddedServer extends BeforeAndAfterAll with Http4sDsl[IO] {
self: Suite =>
private var server: Server[IO] = _
protected var lastRequest: Request[IO] = _
private def captureRequest: HttpService[IO] = Kleisli { req: Request[IO] =>
lastRequest = req
service(req)
}
override protected def beforeAll(): Unit = {
server = BlazeBuilder[IO]
.bindAny()
.mountService(captureRequest, "/")
.start
.unsafeRunSync()
super.beforeAll()
}
override protected def afterAll(): Unit = {
super.afterAll()
server.shutdownNow()
}
def address: InetSocketAddress = server.address
def rootURI: String = s"http:/$address"
def service: HttpService[IO]
}

然后我们将其混合到我们的client规范中

类似的东西

class SomeRequesterSpec extends WordSpec with EmbeddedServer {
override def service: HttpService[IO] = HttpService[IO] {
case GET -> Root / "failure" => ServiceUnavailable()
case GET -> Root / "success" => Ok(SuccessBody)
case GET -> Root / "partial-success" => Ok(PartialSuccessBody)
case GET -> Root / "malformed" => Ok(MalformedBody)
case GET -> Root / "empty" => Ok(EmptyResponse)
}
//... you specs go here
}

在您的规范中,您可以使用客户端调用您的mocked服务器 使用s"$rootURI/success"s"$rootURI/failure"终结点,并检查它是否正确处理响应。 此外lastRequestvar 总是有最后一个请求问题,所以你可以对它运行断言,比如

lastRequest.headers should contain(`Accept-Encoding`(ContentCoding.gzip))

这种方法对我们来说非常有效,我们可以测试客户端是否处理来自服务器的各种输出以及他们所做的所有操作

最新更新