为什么此 akka-http 路由测试从未成功完成?
Why does this akka-http route test never complete successfully?
我有一个简单的路线和一些单独成功的测试,但由于超时而集体失败。知道为什么吗?
val route = (requestHandler: ActorRef @@ Web) => {
get {
pathPrefix("apps") {
pathEndOrSingleSlash {
completeWith(implicitly[ToEntityMarshaller[List[String]]]) { callback =>
requestHandler ! GetAppsRequest(callback)
}
} ~ path("stats") {
completeWith(implicitly[ToEntityMarshaller[List[Stats]]]) { callback =>
requestHandler ! GetStatsRequest(callback)
}
}
} ~ path("apps" / Segment / "stats") { app =>
completeWith(implicitly[ToEntityMarshaller[Stats]]) { callback =>
requestHandler ! GetStatsForOneRequest(app, callback)
}
}
}
}
和测试:
val testProbe = TestProbe()
val testProbeActor = testProbe.ref
.taggedWith[Web]
val timeout = 1.minute
"Route" should "respond to get apps request" in {
implicit val routeTestTimout = RouteTestTimeout(timeout.dilated)
Get("/apps") ~> route(testProbeActor) ~> check {
testProbe.receiveOne(timeout) match {
case GetAppsRequest(callback) => {
callback(k8SProperties.apps)
}
}
entityAs[List[String]] should contain("test")
}
testProbe.expectNoMessage(timeout)
}
it should "respond to get stats request for all apps" in {
implicit val routeTestTimout = RouteTestTimeout(timeout.dilated)
val app = "test"
Get("/apps/stats") ~> route(testProbeActor) ~> check {
testProbe.receiveOne(timeout) match {
case GetStatsRequest(callback) => {
callback(List(Stats(app, ChronoUnit.SECONDS, Nil)))
}
case other => fail(s"Unexpected message $other.")
}
entityAs[List[Stats]].size shouldBe (1)
entityAs[List[Stats]].head.app shouldBe (app)
}
testProbe.expectNoMessage(timeout)
}
it should "respond to get stats request for one app" in {
implicit val routeTestTimout = RouteTestTimeout(timeout.dilated)
val app = "test"
Get(s"/apps/$app/stats") ~> route(testProbeActor) ~> check {
testProbe.receiveOne(timeout) match {
case GetStatsForOneRequest(app, callback) => {
callback(Stats(app, ChronoUnit.SECONDS, Nil))
}
case other => fail(s"Unexpected message $other.")
}
entityAs[Stats].app shouldBe (app)
}
testProbe.expectNoMessage(timeout)
}
问题是您在所有三个测试中使用单个 TestProbe。该 TestProbe 是单个参与者,因此会接收来自所有三个测试的消息。如果您只是将测试探针的创建和配置移动到测试主体中,它应该会按预期工作;特别是这两行:
val testProbe = TestProbe()
val testProbeActor = testProbe.ref
.taggedWith[Web]
工作代码,感谢我。
"Routes" should "respond to get apps request" in {
testProbe.setAutoPilot((_: ActorRef, msg: Any) => {
msg match {
case GetAppsRequest(callback) => callback(List("test")); TestActor.KeepRunning
case _ => TestActor.NoAutoPilot
}
})
Get("/apps") ~> handler ~> check {
entityAs[List[String]] should contain("test")
}
}
将 TestProbe
放入 check
中会永远挂起,这可能是因为它造成了死锁;测试等待回调被调用,直到主体被执行才调用回调。
使用自动驾驶仪设置一个 "expectation" 可以稍后实现从而避免死锁。
我有一个简单的路线和一些单独成功的测试,但由于超时而集体失败。知道为什么吗?
val route = (requestHandler: ActorRef @@ Web) => {
get {
pathPrefix("apps") {
pathEndOrSingleSlash {
completeWith(implicitly[ToEntityMarshaller[List[String]]]) { callback =>
requestHandler ! GetAppsRequest(callback)
}
} ~ path("stats") {
completeWith(implicitly[ToEntityMarshaller[List[Stats]]]) { callback =>
requestHandler ! GetStatsRequest(callback)
}
}
} ~ path("apps" / Segment / "stats") { app =>
completeWith(implicitly[ToEntityMarshaller[Stats]]) { callback =>
requestHandler ! GetStatsForOneRequest(app, callback)
}
}
}
}
和测试:
val testProbe = TestProbe()
val testProbeActor = testProbe.ref
.taggedWith[Web]
val timeout = 1.minute
"Route" should "respond to get apps request" in {
implicit val routeTestTimout = RouteTestTimeout(timeout.dilated)
Get("/apps") ~> route(testProbeActor) ~> check {
testProbe.receiveOne(timeout) match {
case GetAppsRequest(callback) => {
callback(k8SProperties.apps)
}
}
entityAs[List[String]] should contain("test")
}
testProbe.expectNoMessage(timeout)
}
it should "respond to get stats request for all apps" in {
implicit val routeTestTimout = RouteTestTimeout(timeout.dilated)
val app = "test"
Get("/apps/stats") ~> route(testProbeActor) ~> check {
testProbe.receiveOne(timeout) match {
case GetStatsRequest(callback) => {
callback(List(Stats(app, ChronoUnit.SECONDS, Nil)))
}
case other => fail(s"Unexpected message $other.")
}
entityAs[List[Stats]].size shouldBe (1)
entityAs[List[Stats]].head.app shouldBe (app)
}
testProbe.expectNoMessage(timeout)
}
it should "respond to get stats request for one app" in {
implicit val routeTestTimout = RouteTestTimeout(timeout.dilated)
val app = "test"
Get(s"/apps/$app/stats") ~> route(testProbeActor) ~> check {
testProbe.receiveOne(timeout) match {
case GetStatsForOneRequest(app, callback) => {
callback(Stats(app, ChronoUnit.SECONDS, Nil))
}
case other => fail(s"Unexpected message $other.")
}
entityAs[Stats].app shouldBe (app)
}
testProbe.expectNoMessage(timeout)
}
问题是您在所有三个测试中使用单个 TestProbe。该 TestProbe 是单个参与者,因此会接收来自所有三个测试的消息。如果您只是将测试探针的创建和配置移动到测试主体中,它应该会按预期工作;特别是这两行:
val testProbe = TestProbe()
val testProbeActor = testProbe.ref
.taggedWith[Web]
工作代码,感谢我。
"Routes" should "respond to get apps request" in {
testProbe.setAutoPilot((_: ActorRef, msg: Any) => {
msg match {
case GetAppsRequest(callback) => callback(List("test")); TestActor.KeepRunning
case _ => TestActor.NoAutoPilot
}
})
Get("/apps") ~> handler ~> check {
entityAs[List[String]] should contain("test")
}
}
将 TestProbe
放入 check
中会永远挂起,这可能是因为它造成了死锁;测试等待回调被调用,直到主体被执行才调用回调。
使用自动驾驶仪设置一个 "expectation" 可以稍后实现从而避免死锁。