1

I'm trying to test an Actor A inside my Play 2.4 application with Scaldi. This actor is calling injectActorRef[B] that I want to mock with a TestKit.TestProbe.

Inside my specs2, I would like to be able to retrieve the probe for mocked B while providing the corresponding TestKit.TestProbe.ref to actor A.

I would like to do something like this :

implicit val inj = (new TestModule(){
    bind[TestProbe] identifiedBy 'probeForB to TestProbe()
    bind[B] to inject[TestProbe]('probeForB).ref
}).injector

inject[TestProbe]('probeForB).expectMsgType[] must ...

The issue is that the ref is an ActorRef and therefore does not match the expected Btype.

Is there a clean way to do that ? Can we specify an ActorRef to be returned by injectActorRef[B]?


I ended up overriding the bind for Actor A.

val probeForB = TestProbe()

implicit val inj = (new Module() {
    bind[A] to new A() {
       override def injectB(): ActorRef = probeForB.ref
    }
 }).injector
Thomas Pocreau
  • 470
  • 5
  • 12

1 Answers1

1

As you mentioned, the issue is that inject[TestProbe]('probeForB).ref gives you an ActorRef back instead of an instance of actual actor.

If you would like to test it in a way you described, then you need define a binding of ActorRef for actor B as well. For example:

bind [BActor] toProvider new BActor
bind [ActorRef] identifiedBy 'bRef to {
  implicit val system = inject [ActorSystem]
  injectActorRef[BActor]
}

When you have this setup, then you can override the second binding with test probe:

bind [ActorRef] identifiedBy 'bRef to inject[TestProbe]('probeForB).ref

Please note, that this example is not direct equivalent since BActor now has a different supervisor (guardian actor, that's why we need to inject an ActorSystem here).

injectActorRef delegates an actor creation to a "context" (a parent actor or actor system) with context.actorOf. What makes it helpful is that it creates a special Props that injects new instances of BActor class when akka wants to create one. This means that scaldi does not have control over the actor lifecycle (it's pretty important, since akka itself has very sophisticated mechanisms for this), it just gives akka the knowledge how to create new instances of particular actor class.

If you are creating an actor within another actor (like you described), then the lifecycle of them both is strongly connected and managed by akka. That's the reason why in this case you can't simply override and Actor binding with an ActorRef binding for the tests.

tenshi
  • 26,268
  • 8
  • 76
  • 90