为什么 `TestFSMRef.receive 必须抛出 [Exception]` 间歇性地失败
Why does `TestFSMRef.receive must throw[Exception]` fail intermittently
嗨,各位程序员和受人尊敬的大师们,
我有一个实现 FSM
的 actor,它需要在特定状态(Busy)中对某些消息抛出 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 只有当 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 版本 2.3.13
谁能解释为什么有时调用 myTestActorRef.receive(ExceptionResponse(testedCode))
不会导致 Exception
?
这确实是一个棘手的问题:我的主要怀疑是 Actor 尚未初始化。为什么是这样?在实现 system.actorOf
(被 TestFSMRef.apply()
使用)时,很明显只能有一个实体负责实际启动一个 Actor,那就是它的父实体。我尝试了很多不同的东西,但它们都在某种程度上存在缺陷。
但这怎么会导致测试失败呢?
基本答案是,使用您显示的代码不能保证在您执行 setState
时 FSM 已经初始化。特别是在(低功率)Jenkins 盒子上,监护人 actor 可能在可测量的时间内没有被安排到 运行。如果是这种情况,那么 FSM 中的 startWith
语句将覆盖 setState
,因为它之后 运行s。
解决这个问题的方法是向 FSM 发送另一条消息,并在调用 setState
之前期望返回正确的响应。
嗨,各位程序员和受人尊敬的大师们,
我有一个实现 FSM
的 actor,它需要在特定状态(Busy)中对某些消息抛出 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 只有当 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 版本 2.3.13
谁能解释为什么有时调用 myTestActorRef.receive(ExceptionResponse(testedCode))
不会导致 Exception
?
这确实是一个棘手的问题:我的主要怀疑是 Actor 尚未初始化。为什么是这样?在实现 system.actorOf
(被 TestFSMRef.apply()
使用)时,很明显只能有一个实体负责实际启动一个 Actor,那就是它的父实体。我尝试了很多不同的东西,但它们都在某种程度上存在缺陷。
但这怎么会导致测试失败呢?
基本答案是,使用您显示的代码不能保证在您执行 setState
时 FSM 已经初始化。特别是在(低功率)Jenkins 盒子上,监护人 actor 可能在可测量的时间内没有被安排到 运行。如果是这种情况,那么 FSM 中的 startWith
语句将覆盖 setState
,因为它之后 运行s。
解决这个问题的方法是向 FSM 发送另一条消息,并在调用 setState
之前期望返回正确的响应。