Search code examples
scalaakkafsm

How to construct akka TestFSMRef with parameters?


I am trying to test an Akka FSM using the information provided here. However I cannot figure out how to create a TestFSMRef when my subclass of Actor with FSM requires parameters for instantiation.

For standard, non-FSM tests, I create the TestActorRef via:

val testActor = TestActorRef(MyActorFSM.props("nl", p1, p2))

where the .props method is implemented as per the documented Recommended Practice. I have tried instantiating a testActor and then passing that to the TestFSMRef constructor:

val fsm = TestFSMRef(testActor)

but that does not compile:

inferred type arguments [Nothing,Nothing,akka.testkit.TestActorRef[Nothing]]
do not conform to method apply's type parameter bounds [S,D,T <: akka.actor.Actor]

Solution

  • Stealing the code from akka's example FSM actor, I tweaked it a bit to have two constructor params and it now looks like this:

    class MyFSMActor(foo:String, bar:Int) extends Actor with FSM[State,Data]{
      println(s"My foo = $foo and my bar = $bar")
      startWith(Idle, Uninitialized)
    
      when(Idle) {
        case Event(SetTarget(ref), Uninitialized) =>
          stay using Todo(ref, Vector.empty)
      }
    
      // transition elided ...
    
      when(Active, stateTimeout = 1 second) {
        case Event(Flush | StateTimeout, t: Todo) =>
          goto(Idle) using t.copy(queue = Vector.empty)
      }
    
      // unhandled elided ...
    
      initialize()
    }
    

    Then, I can create a test ref to it like so:

    val test = TestFSMRef(new MyFSMActor("hello", 1))
    println(test.stateName)
    

    When I do that, I see:

    My foo = hello and my bar = 1
    Idle
    

    You normally don't invoke the Actors constructor directly (and it will fail if you do), but wrapping it in a TestActorRef or a TestFSMRef will allow you to get around that limitation. I hope this helps you get your code working.