I am creating the router from Akka configuration.
val router = context.actorOf(FromConfig.props(MyActor.props), "router")
I want to unit test the Actor that the router is in, and being able to inject the router into the Actor would be helpful.
Is it possible to instead inject this router using Scaldi? I know in the Scaldi module I can bind using new.
binding toProvider new OrderProcessor
But I can't seem to find a way to create bindings from config.
The properties can be injected.
In the Module
binding identifiedBy "props-from-config" to FromConfig.props(MyActor.props)
And in the Actor inject the props and create the actor.
private val propsFromConfig = inject[Props]("props-from-config")
val router: ActorRef = context.actorOf(propsFromConfig, "router")
Then in the unit test bind any props. The Actor is creating an actor from props, and does not know that the props are coming from config.
Related
First of all, I am not asking how to unit test an Akka Actor. I know the techniques to do that. My problem is slightly different.
I am developing a Scala program, let's call it the client, that does not use Akka Actors. However, the program uses a library, let's call it the server, whose interface is implemented using an Akka Actor.
Then, through the ask pattern, the client interacts with the server.
// This is the client program
class Client(uri: String) {
implicit val context: ActorSystem = ActorSystem("client-actor-system")
private val mainActor = context.actorSelection(uri)
def connect: Future[SomeClass] = {
implicit val timeout: Timeout = Timeout(5 seconds)
(mainActor ? Connect()).mapTo[CreationResponse]
}
}
Now, I want to write down some unit tests for Client class. Unit testing means to test a class in isolation. Every external dependency should be mocked.
How can I mock the reference to mainActor inside Client class? How can I inject in the actor system a mock actor for mainActor?
Thanks in advance.
I would inject the mainActor actor selection into the client, and create that using Akka TestKits.
Of course, you need an ActorSelection, not an ActorRef. Some solutions for that can be found here:
Giving an ActorPath for a testProbe
Or alternatively, you could make mainActor an ActorRef by sending an Identify message to the ActorSelection.
I'm exploring Macwire DI framework for Scala and while doing it I faced a problem.
I have a dispatcher actor that creates a bunch of actors that are dependent on the dispatcher. Dispatcher controls all the message flow between its child actors.
Here's a brief situation of my setup:
class WorkerActor(dispatcher: ActorRef) extends Actor {}
class Dispatcher extends Actor {
private val worker = context.actorOf(Props(
new WorkerActor(self)
))
}
In my real project WorkerActor has more dependencies than only one “self”. And they can be easily wired.
I tried doing this dispatcher: ActorRef ## Dispatcher, but it gave me error:
Error:(47, 9) Cannot find a value of type: [akka.actor.ActorRef ##
Dispatcher]
wire[WorkerActor]
If I was using guice, this would work like a charm:
bind[AkkaRef] annotatedWith(Names.named("Dispatcher")) toInstance self
The question is: how do I add “self” to the scope of macwire?
Or maybe I should use different approach?
Thanks!
Using Scaldi I want to inject Actors into other Actors. When should I use inject vs injectActorRef?
For example, in the Module I have a binding that looks like this: binding toProvider new SomeActor(). When injecting SomeActor which method should I use?
It seems like i am able to inject Services or Akka Actors without the use of Module. May i know what is the purpose of Module then?
class Hello(implicit inj:Injector) extends Controller with AkkaInjectable {
val greetingService = inject[GreetingService]
implicit val system = inject [ActorSystem]
val greetingActor = injectActorRef[greetingActor]
def greet(person:Person) = Action {
Ok(greetingService.greet(person.name))
}
}
Even without the below it works just fine
class MainModule extends Module {
binding to new GreetingService
bind [ActorSystem] to ActorSystem("ScaldiAkkaExample") destroyWith (_.terminate())
binding toProvider new StatisticsProvider
}
Module basically instantiates the services and when you inject them to your controllers, all controllers share the same instance (which is what we want).
Without declaring the bindings in module you will have a new instance of service in every controller (which is not what we want).
However in Akka its the opposite
Quote from scaldi
I would like to point out how Actor are bound. It is important, that you bind them with toProvider function. It will make sure that Scaldi always creates new instances of the Actor classes when you inject them with injectActorRef or injectActorProps
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
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.