I am trying to factor some common tests of some common Akka FSM code into a trait but the sender
ref is becoming deadLetters
. Using this FSM code:
import akka.actor.FSM
import akka.testkit.TestFSMRef
import akka.testkit.TestKit
import akka.actor.ActorSystem
import org.scalatest.Matchers
import org.scalatest.WordSpecLike
import akka.testkit.ImplicitSender
sealed trait MyState
case object StateA extends MyState
case object StateB extends MyState
case class MyData(ignored: Option[Int] = None)
class MyFSM extends FSM[MyState, MyData] {
startWith(StateA, MyData())
val common: StateFunction = {
case Event(_, _) =>
System.err.println(s"sender is $sender")
sender ! s"hello $stateName"
stay
}
when(StateA)(common)
when(StateB)(common)
}
Then I can test the common code to the two states in two specs with duplicated test code using:
class StateASpec extends TestKit(ActorSystem()) with WordSpecLike with Matchers with ImplicitSender {
def testCommonBehaviour(testState: MyState) {
val fsm = TestFSMRef(new MyFSM())
fsm.setState(testState, MyData())
fsm ! "hello fsm"
expectMsg("hello StateA")
}
"StateA" should {
"respond to common test case" in {
testCommonBehaviour(StateA)
}
}
}
class StateBSpec extends TestKit(ActorSystem()) with WordSpecLike with Matchers with ImplicitSender {
def testCommonBehaviour(testState: MyState) {
val fsm = TestFSMRef(new MyFSM())
fsm.setState(testState, MyData())
fsm ! "hello fsm"
expectMsg("hello StateB")
}
"StateB" should {
"respond to common test case" in {
testCommonBehaviour(StateB)
}
}
}
when I try to reactor the common test logic out into a trait with:
trait TraitOfTests {
self: TestKit with ImplicitSender =>
def testCommonBehaviour(testState: MyState) {
val fsm = TestFSMRef(new MyFSM())
fsm.setState(testState, MyData())
fsm ! "hello fsm"
expectMsg("hello $testState")
}
}
class StateASpec extends TestKit(ActorSystem()) with WordSpecLike with Matchers with ImplicitSender with TraitOfTests {
"StateA" should {
"respond to common test case" in {
testCommonBehaviour(StateA)
}
}
}
class StateBSpec extends TestKit(ActorSystem()) with WordSpecLike with Matchers with ImplicitSender with TraitOfTests {
"StateB" should {
"respond to common test case" in {
testCommonBehaviour(StateB)
}
}
}
The responses go to the deadLetters actor:
sender is Actor[akka://default/deadLetters] [INFO] [11/30/2014 16:15:49.812] [default-akka.actor.default-dispatcher-2] [akka://default/deadLetters] Message [java.lang.String] from TestActor[akka://default/user/$$b] to Actor[akka://default/deadLetters] was not delivered. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
How can I fix up the implicit sender to ensure that when the FSM actor responds to the call of the test make from within the trait it gets back to the testActor so that the expectMsg
works?
One way to fix this problem is a slight re-design to your common trait like so:
If you define the implicit sender on
testCommonBehavior
then you can be assured that it will properly pick it up from your tests that make it available viaImplicitSender