使用Scala进行Akka actors单元测试

9

我对Scala还比较新,请多关照。

在我目前正在构建的应用程序中,我正在使用Akka actors,并且想编写一些单元测试。我遇到了这个Akka actors编写单元测试的官方文档

但我无法确切理解它应该如何工作。特别是,

val actorRef = TestActorRef(new MyActor)
// hypothetical message stimulating a '42' answer
val future = actorRef ? Say42
val Success(result: Int) = future.value.get
result must be(42)

当我尝试时,我得到了“未找到:值Success”的结果,这并不奇怪。
然后我发现了如何测试Scala actors的示例
val actorRef = TestActorRef[TickTock]

implicit val timeout = Timeout(5 seconds)
val future = (actorRef ? new Tick("msg")).mapTo[String]
val result = Await.result(future, timeout.duration)

Assert.assertEquals("processed the tick message", result)

我承认这可能有点老旧,但它易于理解,更接近我通常在使用 Futures 时所用的方式,而且最重要的是它能够工作。它确实需要我声明一些 implicit,例如 ActorSystem、timeout 等等,而官方方法似乎不需要...

如果可能的话,我想使用官方文档提出的方法,因此我希望有人能帮助我理解它的工作原理(特别是 Success 部分)以及如何使用它。


1
你有像scala.util._或者scala.util.Success这样的导入吗? - 4lex1v
3个回答

13

由于不知道您对Scala有多少了解,回答您的问题可能会太长。我会尽量让我的回答简短,但请随时要求澄清。我也代表整个stackoverflow社区向您道歉,因为在提问之前显然缺乏技能而感到需要道歉。

在Scala 2.10中引入了Try的概念。它非常类似于OptionOption是处理null值的概念。类型为Option的值可以采用两种形式:Some(value)None。当您拥有可选值Option时,可以在其上进行模式匹配,以查看它是Some还是None,然后相应地采取行动。模式匹配在Scala中的许多地方发生,其中之一是在val的初始化期间。以下是几个示例:

val x = 10 // pattern 'x' on the LHS matches any value on the RHS so 'x' is initialized with 10
val Some(x) = Some(10) // pattern 'Some(x)' on the LHS matches any value of type 'Some' and binds it's value to x, so 'x' is yet again initialized with 10

Try是处理异常的概念。类型为Try的值可以有两种形式:Success(result)Failure(throwable)。当你有一个类型为Try的值时,你可以对它进行模式匹配以查看它是Success还是Failure

这就是你的代码中发生的事情(在Success上进行模式匹配)。与Option相比,默认情况下不会引入两种形式的Try,这会导致编译错误。以下代码可以解决这个问题:

import scala.util.{Try, Success, Failure}

很形象的比喻; 我对于模式匹配和选项理解得相当好,能够跟上进度。只是我对Try、Success和Failure毫无头绪。那么,Future.value.get返回类型为Try的值吗? 换句话说,以下示例代码:`val futureValue = future.value.get futureValue match { case Success(x:Int) => x must be(42) case _ => false must be(true) //just fail }`以上代码在我的测试中可以工作:) - lloydmeta
1
是的,完全相同的事情,但你绝对应该采用 Viktor Klang 建议的方法。 - agilesteel
谢谢,我会接受你的答案,因为它最清楚地回答了我的问题并消除了我的困惑。 - lloydmeta

5

让你的测试扩展TestKit,然后再添加“with ImplicitSender”,这样你就可以做一些事情,例如:

val yourActor = system.actorOf(Props[MyActor])
yourActor ! Say42
expectMsg(42)

谢谢,这真的很有帮助。经过一些搜索,我找到了这个示例,演示了如何使用ImplicitSender与DefaultTimeout等。 - lloydmeta

4

首先,使用get获取future的值不是一个好的模式,如果失败可能会引发异常。您应该使用Await.result,就像您第二个示例中一样,或者使用模式匹配来处理Success和Failure:

future match {
  case Success(value) => // work with value
  case Failure(ex) => // work with exception
}

要使用SuccessFailure,请导入scala.util._scala.util.{Success, Failure}

这里是最新版本 2.2-M3 的官方文档。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接