为什么`TestFSMRef.receive必须抛出[Exception]`间歇性失败

时间:2015-08-13 07:16:59

标签: scala akka akka-testkit

嗨同伴和钦佩的大师,

我有一个实现FSM的actor,当某个特定状态( Busy )要由其 Supervisor重新启动时,需要对某些消息抛出IOException

摘录:

case class ExceptionResonse(errorCode: Int)


when(Busy) {
    case ExceptionResponse(errorCode) =>
      throw new IOException(s"Request failed with error code $errorCode")
}

我尝试使用TestActorRef并直接调用receive来测试该行为,期望接收投掷IOException

case class WhenInStateBusy() extends TestKit(ActorSystem()) with After {
  val myTestFSMRef = TestFSMRef(MyFSM.props)

  ...

  def prepare: Result = {
    // prepares tested actor by going through an initialization sequence
    // including 'expectMsgPfs' for several messages sent from the tested FSM
    // most of my test cases depend on the correctness of that initialization sequence

    // finishing with state busy
    myTestFSMRef.setState(Busy)

    awaitCond(
      myTestFSMRef.stateName == Busy, 
      maxDelay, 
      interval, 
      s"Actor must be in State 'Busy' to proceed, but is ${myTestFSMRef.stateName}"
    )
    success
  }

  def testCase = this {
    prepare and {
      myTestFSMRef.receive(ExceptionResponse(testedCode)) must throwAn[IOException]
    }
  }
}

注意:初始化序列确保测试的FSM已完全初始化并已设置其内部可变状态。状态忙碌只能在actor接收到某种消息时才会离开,该消息在我的测试设置中必须由测试用例提供,所以我很确定FSM处于正确的状态。

现在,在我的Jenkins服务器(Ubuntu 14.10)上,这个测试用例在20次尝试中大约有1次失败( - >没有抛出异常)。但是,在我的开发机器(Mac Os X 10.10.4)上,我无法重现该错误。所以调试器对我没用。

测试按顺序运行,并在每个示例后关闭测试系统。

  • Java版本1.7.0_71
  • Scala版本2.11.4
  • Akka版本2.3.6
  • Specs2 version 2.3.13

有人可以解释为什么有时调用myTestActorRef.receive(ExceptionResponse(testedCode))不会导致Exception

1 个答案:

答案 0 :(得分:2)

这确实是一个棘手的问题:我的主要嫌疑人是演员尚未初始化。为什么是这样?当实现system.actorOf(由TestFSMRef.apply()使用)时,很明显只有一个实体负责实际启动一个Actor,那就是它的父级。我尝试了许多不同的东西,但都以某种方式存在缺陷。

但是这怎么会让这个测试失败呢?

基本答案是,使用您显示的代码无法保证在执行setState时FSM已经初始化。特别是在(低功率)Jenkins盒子上,可能是守护演员没有被计划在可测量的时间内运行。如果是这种情况,则FSM中的startWith语句将覆盖setState,因为它会在之后运行。

解决方法是向FSM发送另一条消息,并在调用setState之前预期回复正确的响应。