【问题标题】:Why does `TestFSMRef.receive must throw[Exception]` fail intermittently为什么`TestFSMRef.receive must throw [Exception]`间歇性失败
【发布时间】:2015-11-06 01:03:17
【问题描述】:

各位程序员和敬佩的大师们,

我有一个实现 FSM 的参与者,它需要在特定状态(忙碌)由其主管重新启动时对某些消息抛出 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 已完全初始化并已设置其内部可变状态。状态 Busy 只能在参与者收到某种消息时才能离开,在我的测试设置中必须由测试用例提供该消息,所以我很确定 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 版本 2.3.13

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

【问题讨论】:

  • 您没有显示myTestActorRef 的创建位置。是myTestFSMRef 的错字还是两个不同的对象?
  • @mattinbits 是的,这是一个错字,已解决问题
  • 您对 awaitCond 的看法是对的,我正在查看 Java API。不过,AssertException 会让您的测试失败,对吧?
  • 是的,AssertionException 使测试失败。
  • 我在this question 的答案变体中找到了解决我的问题的方法。但是,我仍然不明白为什么这不起作用。

标签: scala akka akka-testkit


【解决方案1】:

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

但这怎么会导致这个测试失败呢?

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

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

【讨论】:

  • 谢谢,这听起来完全合理 :) 不过,我得回办公室看看实际的测试用例是否这样做。
  • 恐怕不是这样。在开始实际测试之前,测试用例期望来自被测参与者的其他几条消息,而无需使用状态机进行调整。我已经更新了我的问题,以尽可能接近实际代码,因为我很遗憾无法粘贴实际代码本身。
  • 那么唯一的其他解释是 FSM 在后台做了一些覆盖状态的事情。一般来说,不建议混合使用同步和异步交互,并且作为 TestFSMRef 并不能确保所有消息都得到同步处理,值得注意的例外是主管操作和外部交互。
猜你喜欢
  • 2017-07-05
  • 1970-01-01
  • 2019-04-18
  • 2011-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-19
  • 1970-01-01
相关资源
最近更新 更多