根据 IO 和 TestActorRef
Akka IO and TestActorRef
如果我需要通过 spray-can 编写涉及 HTTP 请求的集成测试,我如何确保 spray-can 使用 CallingThreadDispatcher?
目前以下演员将打印None
class Processor extends Actor {
override def receive = {
case Msg(n) =>
val response = (IO(Http) ? HttpRequest(GET, Uri("http://www.google.com"))).mapTo[HttpResponse]
println(response.value)
}
}
如何确保请求与测试在同一线程上执行(导致同步请求)?
这似乎是进行集成内部测试的奇怪方式,因为您不模拟 "Google",所以更像是集成外部测试和同步 TestActorRef
不太适合这里.在 spray 中控制线程的要求也很棘手。但是,如果您真的需要它来进行 http-request - 这是可能的。在一般情况下,您必须在 application.conf
:
中设置多个调度程序
- "manager-dispatcher"(来自 Http.scala)派遣您的
IO(Http) ? req
- "host-connector-dispatcher" 由
HttpHostConnector
(或 ProxyHttpHostConnector
)实际发送您的请求
- "settings-group-dispatcher" 对于
Http.Connect
它们都在Configuration Section of spray documentation. And they all are pointing to "akka.actor.default-dispatcher" (see Dispatchers)中描述,所以你可以通过改变这个来改变它们。
这里的问题是调用线程实际上不能保证是你的线程,所以它对你的测试没有多大帮助。试想一下,如果一些参与者注册了一些处理程序,响应您的消息:
//somewhere in spray...
case r@Request => registerHandler(() => {
...
sender ! response
})
响应可能是从另一个线程发送的,所以response.value
在当前可能仍然是None
。实际上,响应将从底层套接字库的侦听线程发送,独立于您的测试线程。简单地说,请求可能在一个(您的)线程中发送,但响应在另一个线程中接收。
如果您真的真的需要在这里阻塞,我建议您将此类代码示例(如 IO(Http) ? HttpRequest
)移出并在测试中以任何方便的方式模拟它们。像那样发短信:
trait AskGoogle {
def okeyGoogle = IO(Http) ? HttpRequest(GET, Uri("http://www.google.com"))).mapTo[HttpResponse]
}
trait AskGoogleMock extends AskGoogle {
def okeyGoogle = Await.result(super.okeyGoogle, timeout)
}
class Processor extends Actor with AskGoogle {
override def receive = {
case Msg(n) =>
val response = okeyGoogle
println(response.value)
}
}
val realActor = system.actorOf(Props[Processor])
val mockedActor = TestActorRef[Processor with AskGoogleMock]
顺便说一句,您可以使用另一个 TestActorRef
模拟 IO(HTTP)
到自定义 actor,它将为您执行外部请求 - 如果您有一个大项目,它应该需要最少的代码更改.
如果我需要通过 spray-can 编写涉及 HTTP 请求的集成测试,我如何确保 spray-can 使用 CallingThreadDispatcher?
目前以下演员将打印None
class Processor extends Actor {
override def receive = {
case Msg(n) =>
val response = (IO(Http) ? HttpRequest(GET, Uri("http://www.google.com"))).mapTo[HttpResponse]
println(response.value)
}
}
如何确保请求与测试在同一线程上执行(导致同步请求)?
这似乎是进行集成内部测试的奇怪方式,因为您不模拟 "Google",所以更像是集成外部测试和同步 TestActorRef
不太适合这里.在 spray 中控制线程的要求也很棘手。但是,如果您真的需要它来进行 http-request - 这是可能的。在一般情况下,您必须在 application.conf
:
- "manager-dispatcher"(来自 Http.scala)派遣您的
IO(Http) ? req
- "host-connector-dispatcher" 由
HttpHostConnector
(或ProxyHttpHostConnector
)实际发送您的请求 - "settings-group-dispatcher" 对于
Http.Connect
它们都在Configuration Section of spray documentation. And they all are pointing to "akka.actor.default-dispatcher" (see Dispatchers)中描述,所以你可以通过改变这个来改变它们。
这里的问题是调用线程实际上不能保证是你的线程,所以它对你的测试没有多大帮助。试想一下,如果一些参与者注册了一些处理程序,响应您的消息:
//somewhere in spray...
case r@Request => registerHandler(() => {
...
sender ! response
})
响应可能是从另一个线程发送的,所以response.value
在当前可能仍然是None
。实际上,响应将从底层套接字库的侦听线程发送,独立于您的测试线程。简单地说,请求可能在一个(您的)线程中发送,但响应在另一个线程中接收。
如果您真的真的需要在这里阻塞,我建议您将此类代码示例(如 IO(Http) ? HttpRequest
)移出并在测试中以任何方便的方式模拟它们。像那样发短信:
trait AskGoogle {
def okeyGoogle = IO(Http) ? HttpRequest(GET, Uri("http://www.google.com"))).mapTo[HttpResponse]
}
trait AskGoogleMock extends AskGoogle {
def okeyGoogle = Await.result(super.okeyGoogle, timeout)
}
class Processor extends Actor with AskGoogle {
override def receive = {
case Msg(n) =>
val response = okeyGoogle
println(response.value)
}
}
val realActor = system.actorOf(Props[Processor])
val mockedActor = TestActorRef[Processor with AskGoogleMock]
顺便说一句,您可以使用另一个 TestActorRef
模拟 IO(HTTP)
到自定义 actor,它将为您执行外部请求 - 如果您有一个大项目,它应该需要最少的代码更改.