1

嗨,程序员们和受人尊敬的大师们,

我有一个执行者,它需要在处于特定状态(忙碌FSM)时在某些消息上引发 IOException以由其Supervisor重新启动。

摘抄:

case class ExceptionResonse(errorCode: Int)


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

我正在尝试通过使用 aTestActorRef并直接调用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 已完全初始化并已设置其内部可变状态。只有当参与者收到在我的测试设置中必须由测试用例提供的某种消息时才能离开状态忙碌,所以我很确定 FSM 处于正确的状态。

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

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

  • Java 版本 1.7.0_71
  • 斯卡拉版本 2.11.4
  • Akka 版本 2.3.6
  • Specs2 版本 2.3.13

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

4

1 回答 1

2

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

但这如何使这个测试失败呢?

基本答案是,您显示的代码不能保证在您执行setStateFSM 时已经初始化。尤其是在(低功率)Jenkins 盒子上,可能是监护参与者没有安排运行一段可测量的时间。如果是这种情况,那么startWithFSM 中的语句将覆盖 ,setState因为它会在之后运行。

对此的解决方案是向 FSM 发送另一条消息,并在调用之前期望得到正确的响应setState

于 2015-08-15T07:21:06.617 回答