I have an akka FSM actor which uses futures in states. For example:
when(StateA) {
case Event(str: String, _) =>
if (str == "ping") {
Future("await-ping").pipeTo(self)(sender)
goto(AwaitStateA)
} else {
stay() replying "stay-ping"
}
}
when(AwaitStateA) {
case Event(str: String, _) =>
goto(StateA) replying str
}
Tests for actor above using akka testkit:
val adaptation: TestFSMRef[State, Data, _ <: Actor]
"Switch between states A" must {
"should return `await-ping`" in {
adaptation ! "ping"
expectMsg("await-ping")
adaptation.stateName should be(StateA)
}
"should return `stay-ping`" in {
adaptation ! "pong"
expectMsg("stay-ping")
adaptation.stateName should be(StateA)
}
}
Full code for tests you can find on github: https://github.com/azhur/fsmtest
The Problem is that tests failed randomly (sometimes they all passed).
Failures appear in test "should return await-ping
" -> "AwaitStateA was not equal to StateA".
Please help to find where am I mistaken.
I try to run tests from command line and from IDE (Intellij IDEA). Results are the same. When I run each test separately, is hard to catch failure.
The future is running on the global ExecutionContext (that you have imported) and there is a race between that and the calling thread dispatcher that is used by TestFSMRef.
I would not use TestFSMRef here. If it is important to verify the state transitions you can use the FSM transition listener instead. Something like this:
val adaptation: ActorRef = system.actorOf(Props[FsmSwitcher1])
"should return `await-ping`" in {
val transitionListener = TestProbe()
adaptation ! SubscribeTransitionCallBack(transitionListener.ref)
transitionListener.expectMsg(CurrentState(adaptation, StateA))
adaptation ! "ping"
expectMsg("await-ping")
transitionListener.expectMsg(Transition(adaptation, StateA, AwaitStateA))
transitionListener.expectMsg(Transition(adaptation, AwaitStateA, StateA))
}