玩 !2.2.4/Akka:一起运行时测试失败,但单独运行时可以 [英] Play ! 2.2.4 / Akka : tests failed when run together, but ok separately

查看:29
本文介绍了玩 !2.2.4/Akka:一起运行时测试失败,但单独运行时可以的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个控制器,在回答之前询问是一个演员和两个测试用例:

I have a controller that asks an actor before answering and two test cases:

  • 当我运行 play test 时,第二个测试失败
  • 当我运行 play testOnly ApplicationSpecplay testOnly IntegrationSpec 时,都成功
  • When I run play test the second test fails
  • When I run play testOnly ApplicationSpec and play testOnly IntegrationSpec, both succeed

我认为 Akka 系统在第一次测试时关闭,而在第二次测试时没有再次启动,但为什么呢?我该如何解决这个问题?

I think that the Akka system is shut down by the first test and not started again by the second test, but why ? And how can I work around that ?

控制器:

object Application extends Controller {
  implicit val _ = Timeout(3 seconds)
  val gamesManagerRef = Akka.system().actorOf(GamesManager.props)

  def index = Authenticated.async { implicit request =>
    (gamesManagerRef ? GamesManager.ListWaitingGames).map {
      case GamesManager.MultipleOperationOk(games) =>
        Ok(views.html.index(GameInformation.getWaitings(request.jedis)))
    }
  }
}

单元测试:

class ApplicationSpec extends Specification {

  "Application" should {

    "send 404 on a bad request" in new WithApplication{
      route(FakeRequest(GET, "/boum")) must beNone
    }

    "render the index page" in new WithApplication{
      val home = route(FakeRequest(GET, "/")).get

      status(home) must equalTo(OK)
      contentType(home) must beSome.which(_ == "text/html")
      contentAsString(home) must contain ("jumbotron")
    }
  }
}

集成测试:

class IntegrationSpec extends Specification {
  "Application" should {
    "work from within a browser" in new WithBrowser {
      browser.goTo("http://localhost:" + port)
      browser.pageSource must contain("jumbotron")
    }
  }
}

测试与由 play new

两者都执行时给出的内容:

Content given when both are executed :

play.api.Application$$anon$1: Execution exception[[AskTimeoutException: Recipient[Actor[akka://application/user/$a#1274766555]] had already been terminated.]]
    at play.api.Application$class.handleError(Application.scala:293) ~[play_2.10.jar:2.2.1]
    at play.api.test.FakeApplication.handleError(Fakes.scala:203) ~[play-test_2.10.jar:2.2.1]
    at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$12$$anonfun$apply$1.applyOrElse(PlayDefaultUpstreamHandler.scala:165) ~[play_2.10.jar:2.2.1]
    at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$12$$anonfun$apply$1.applyOrElse(PlayDefaultUpstreamHandler.scala:162) ~[play_2.10.jar:2.2.1]
    at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:33) ~[scala-library.jar:na]
    at scala.util.Failure$$anonfun$recover$1.apply(Try.scala:185) ~[scala-library.jar:na]
Caused by: akka.pattern.AskTimeoutException: Recipient[Actor[akka://application/user/$a#1274766555]] had already been terminated.
    at akka.pattern.AskableActorRef$.ask$extension(AskSupport.scala:134) ~[akka-actor_2.10.jar:2.2.0]
    at akka.pattern.AskableActorRef$.$qmark$extension(AskSupport.scala:146) ~[akka-actor_2.10.jar:2.2.0]
    at controllers.Application$$anonfun$index$1.apply(Application.scala:24) ~[classes/:na]
    at controllers.Application$$anonfun$index$1.apply(Application.scala:23) ~[classes/:na]
    at controllers.Application$Authenticated$$anonfun$invokeBlock$3.apply(Application.scala:74) ~[classes/:na]
    at controllers.Application$Authenticated$$anonfun$invokeBlock$3.apply(Application.scala:69) ~[classes/:na]

Play 生成的错误页面是怎么回事!到不包含我的jumbotron"的测试

What gives the error page generated by Play! to the test which do not contain my "jumbotron"

我尝试创建新的 FakeApplication 以在 WithBrowser 构造函数中给出,但只发生空页面.

I tried to create new FakeApplication to give in the WithBrowser constructor, but only empty page happens.

完整代码源:https://github.com/Isammoc/yinyang/8cf8ad625b7ef35423f17503a2a35fe390352d22

推荐答案

问题是您在对象的 val 中持有对游戏管理器 actor ref 的引用.运行的第一个测试将初始化这个对象,这将获取当前运行的actor系统并查找actor.然后actor系统将关闭,actor ref将失效.但是应用程序控制器仍然在 val 中持有无效的 ref,所以当下一次测试运行时,它使用来自关闭的 actor 系统的无效 actor ref.

The problem is that you are holding a reference to games manager actor ref in a val in an object. The first test that runs will initialise this object, which will get the currently running actor system and lookup the the actor. Then the actor system will be shutdown, and that actor ref will become invalid. But the application controller still holds the invalid ref in the val, so when the next test runs, it uses the invalid actor ref from the shut down actor system.

以下是您可以解决的方法:

Here are the ways you can solve it:

  • gamesManagerRef改成def,使用actorFor查找并创建(使用actorOf) 在 Global.onStart 中.所以actor在Global.onStart中被创建一次,每次使用时都会进行查找.
  • 使 gamesManagerRef 成为其他对象中的 @volatile var,并让您的 Global.onStart 方法创建角色,然后将其分配给那个 var.这样,每次使用时都不必进行查找,但是有点难看,因为您拥有这个全局共享的可变状态.
  • 我的首选解决方案是编写一个插件(http://developer.vz.net/2012/03/16/writing-a-play-2-0-module/) 在插件启动时查找actor ref,或将其放入在惰性 val 中,然后让消费者通过执行插件查找来访问 actor ref.
  • Change gamesManagerRef to a def, use actorFor to look it up, and create it (using actorOf) in Global.onStart. So the actor is created once in Global.onStart, and the lookup is done every time it's used.
  • Make gamesManagerRef a @volatile var in some other object, and have your Global.onStart method create the actor and then assign it to that var. In this way, the lookup doesn't have to be done each time it's used, but it's kind of ugly because you have this global shared mutable state.
  • My preferred solution is to write a plugin (http://developer.vz.net/2012/03/16/writing-a-play-2-0-module/) that looks up the actor ref when the plugin starts, or puts it in a lazy val, and then have consumers access the actor ref through doing a plugin lookup.

23/07/2018 更新:以上建议已完全过时.推荐的方法在 https://www.playframework.com/documentation/2.6 中有详细记录.x/ScalaAkka.

Update 23/07/2018: The above suggestions are completely out of date. The recommended approaches are well documented in https://www.playframework.com/documentation/2.6.x/ScalaAkka.

这篇关于玩 !2.2.4/Akka:一起运行时测试失败,但单独运行时可以的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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