如何测试客户端 Akka HTTP [英] How to test client-side Akka HTTP

查看:53
本文介绍了如何测试客户端 Akka HTTP的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚开始测试 Akka HTTP 请求级客户端 API(基于未来).我一直在努力弄清楚的一件事是如何为此编写单元测试.有没有办法模拟响应并完成未来而无需实际执行 HTTP 请求?

I've just started testing out the Akka HTTP Request-Level Client-Side API (Future-Based). One thing I've been struggling to figure out is how to write a unit test for this. Is there a way to mock the response and have the future completed without having to actually do an HTTP request?

我正在查看 API 和 testkit 包,试图了解如何使用它们,结果却在文档中找到了它的实际内容:

I was looking at the API and the testkit package, trying to see how I could use that, only to find in the docs that it actually says:

akka-http-testkit 用于验证服务器端服务实现的测试工具和实用程序集

akka-http-testkit A test harness and set of utilities for verifying server-side service implementations

我在想一些 TestServer(有点像 Akka Streams 的 TestSource)并使用服务器端路由 DSL 创建预期的响应并以某种方式将它连接到Http 对象.

I was thinking something TestServer (kinda like the TestSource for Akka Streams) and use the server side routing DSL to create the expected response and somehow hook this up the the Http object.

以下是我要测试的函数功能的简化示例:

Here is a simplified example of what the function does that I want to test:

object S3Bucket {

  def sampleTextFile(uri: Uri)(
    implicit akkaSystem: ActorSystem,
    akkaMaterializer: ActorMaterializer
  ): Future[String] = {
    val request = Http().singleRequest(HttpRequest(uri = uri))
    request.map { response => Unmarshal(response.entity).to[String] }
  }
}

推荐答案

我认为一般来说,您已经意识到最好的方法是模拟响应.在 Scala 中,这可以使用 Scala Mock http://scalamock.org/

I think in general terms you've already hit on the fact that the best approach is to mock the response. In Scala, this can be done using Scala Mock http://scalamock.org/

如果你安排你的代码,使得你的 akka.http.scaladsl.HttpExt 实例被依赖注入到使用它的代码中(例如作为构造函数参数),那么在测试期间你可以注入mock[HttpExt] 的一个实例,而不是使用 Http apply 方法构建的实例.

If you arrange your code so that your instance of akka.http.scaladsl.HttpExt is dependency injected into the code which uses it (e.g. as a constructor parameter), then during testing you can inject an instance of mock[HttpExt] rather than one built using the Http apply method.

我想这是因为不够具体而被否决.这是我将如何构建您的场景的模拟.所有的隐式都使它变得更加复杂.

I guess this was voted down for not being specific enough. Here is how I would structure the mocking of your scenario. It is made a little more complicated by all the implicitis.

main 中的代码:

import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.{Uri, HttpResponse, HttpRequest}
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.stream.ActorMaterializer

import scala.concurrent.{ExecutionContext, Future}

trait S3BucketTrait {

  type HttpResponder = HttpRequest => Future[HttpResponse]

  def responder: HttpResponder

  implicit def actorSystem: ActorSystem

  implicit def actorMaterializer: ActorMaterializer

  implicit def ec: ExecutionContext

  def sampleTextFile(uri: Uri): Future[String] = {

    val responseF = responder(HttpRequest(uri = uri))
    responseF.flatMap { response => Unmarshal(response.entity).to[String] }
  }
}

class S3Bucket(implicit val actorSystem: ActorSystem, val actorMaterializer: ActorMaterializer) extends S3BucketTrait {

  override val ec: ExecutionContext = actorSystem.dispatcher

  override def responder = Http().singleRequest(_)
}

test 中的代码:

import akka.actor.ActorSystem
import akka.http.scaladsl.model._
import akka.stream.ActorMaterializer
import akka.testkit.TestKit
import org.scalatest.{BeforeAndAfterAll, WordSpecLike, Matchers}
import org.scalamock.scalatest.MockFactory
import scala.concurrent._
import scala.concurrent.duration._
import scala.concurrent.Future

class S3BucketSpec extends TestKit(ActorSystem("S3BucketSpec"))
with WordSpecLike with Matchers with MockFactory with BeforeAndAfterAll  {


  class MockS3Bucket(reqRespPairs: Seq[(Uri, String)]) extends S3BucketTrait{

    override implicit val actorSystem = system

    override implicit val ec = actorSystem.dispatcher

    override implicit val actorMaterializer = ActorMaterializer()(system)

    val mock = mockFunction[HttpRequest, Future[HttpResponse]]

    override val responder: HttpResponder = mock

    reqRespPairs.foreach{
      case (uri, respString) =>
        val req = HttpRequest(HttpMethods.GET, uri)
        val resp = HttpResponse(status = StatusCodes.OK, entity = respString)
        mock.expects(req).returning(Future.successful(resp))
    }
  }

  "S3Bucket" should {

    "Marshall responses to Strings" in {
      val mock = new MockS3Bucket(Seq((Uri("http://example.com/1"), "Response 1"), (Uri("http://example.com/2"), "Response 2")))
      Await.result(mock.sampleTextFile("http://example.com/1"), 1 second) should be ("Response 1")
      Await.result(mock.sampleTextFile("http://example.com/2"), 1 second) should be ("Response 2")
    }
  }

  override def afterAll(): Unit = {
    val termination = system.terminate()
    Await.ready(termination, Duration.Inf)
  }
}

build.sbt 依赖:

libraryDependencies += "com.typesafe.akka" % "akka-http-experimental_2.11" % "2.0.1"

libraryDependencies += "org.scalamock" %% "scalamock-scalatest-support" % "3.2" % "test"

libraryDependencies += "org.scalatest" % "scalatest_2.11" % "2.2.6"

libraryDependencies += "com.typesafe.akka" % "akka-testkit_2.11" % "2.4.1"

这篇关于如何测试客户端 Akka HTTP的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆