I have a function which uses scheduleOnce to schedule an event to happen some time in the future, and I want to write a test that checks that:
the event was indeed scheduled
it was scheduled at the correct time
the system behaves as expected when that event eventually triggers
but I don't want the test to actually wait several minutes doing nothing.
How should I best test code that uses akka's Scheduler?
Here is an example of mocking out the scheduler as described by #lmm. In this example, we really test the full scheduling and handling of the action as two separate scenarios. The first testing that given some condition (a message of a certain type being received in my example) we will schedule a callback, and the second being the handling of the message that gets fired back to self when that timer goes off. The code is as follows:
object TimerExampleActor{
case object ClearState
case class ScheduleStateClearing(duration:FiniteDuration)
}
class TimerExampleActor extends Actor{
import TimerExampleActor._
var state:List[Int] = Nil
def receive = {
case ScheduleStateClearing(d) =>
scheduler.scheduleOnce(d, self, ClearState)(context.dispatcher)
case ClearState =>
state = Nil
}
def scheduler = context.system.scheduler
}
Then, using specs2 and mockito, my test case is as follows:
class TimerExampleActorTest extends Specification with Mockito with NoTimeConversions{
import TimerExampleActor._
implicit val system = ActorSystem("test")
trait testscope extends Scope{
val mockScheduler = mock[Scheduler]
val actor = TestActorRef(new TimerExampleActor{
override def scheduler = mockScheduler
})
}
"A request to schedule state clearing" should{
"schedule a callback for the message ClearState to self with the supplied duration" in new testscope{
val dur = 1.minute
actor ! ScheduleStateClearing(dur)
there was one(mockScheduler).scheduleOnce(dur, actor, ClearState)(actor.underlyingActor.context.dispatcher)
}
}
"A ClearState message received by the actor" should{
"clear the interval 'state' List" in new testscope{
actor.underlyingActor.state = List(1,2,3)
actor ! ClearState
actor.underlyingActor.state mustEqual Nil
}
}
}
You can see that when I create the actor instance under test I override the method I created to get me the instance of the scheduler, allowing me to return a mock. This is not the only way to go about testing something like this, but it certainly can be one option for you to consider.
Make the scheduler take a time parameter. In your test use a shorter time than in your regular code.
Or... when testing you can mix in a special trait that modifies your class as needed (shortens the wait time.)
Related
I'm testing an actor that uses an asnychronous future-based API. The actor uses the pipe pattern to send a message to itself when a future completes:
import akka.pattern.pipe
// ...
// somewhere in the actor's receive method
futureBasedApi.doSomething().pipeTo(self)
In my test I mock the API so I control future completion via promises. However, this is interleaved with other messages sent directly to the actor:
myActor ! Message("A")
promiseFromApiCall.success(Message("B"))
myActor ! Message("C")
Now I'm wondering how I can guarantee that the actor receives and
processes message B between message A and C in my test because message B is actually sent in another thread, so I can't control the order
in which the actor's mailbox receives the messages.
I thought about several possible solutions:
sleep after each message for a few milliseconds to make another
order very unlikely
wait for the actor to acknowledge each message, although
acknowledgement is only required for testing
send message B directly to the actor to simulate completion of the
future and write a separate test that ensures that the pipe pattern
is properly used (the test above would not fail if the actor would
not pipe the result message to itself)
I don't really like either of these options but I tend to use the last
one. Is there another better way I can enforce a certain message order in the tests?
Clarification: The question is not how to deal with the fact that messages might be received in random order in production. Controlling the order in the test is essential to make sure that the actor can actually deal with different message orders.
One idea is to define a flag in your actor that indicates whether the actor has received message B. When the actor receives message C, the actor can stash that message C if the flag is false, then unstash it once the actor receives message B. For example:
class MyActor extends Actor with Stash {
def receiveBlock(seenMsgB: Boolean, seenMsgC: Boolean): Receive = {
case MakeApiCall =>
callExternalApi().mapTo[MessageB].pipeTo(self)
case m: MessageB if seenMsgC => // assume msg C has been stashed
unstashAll()
// ...do something with msg B
become(receiveBlock(true, seenMsgC)) // true, true
case m: MessageB if !seenMsgC =>
// ...do something with message B
become(receiveBlock(true, seenMsgC)) // true, false
case m: MessageC if seenMsgB =>
// ...do something with message C
context.become(receiveBlock(seenMsgB, true)) // true, true
case m: MessageC if !seenMsgB =>
stash()
context.become(receiveBlock(seenMsgB, true)) // false, true
case ...
}
def receive = receiveBlock(false, false)
}
After reading a lot more about akka, I finally found a better solution: Replacing the actor mailbox with one I can observe in the tests. This way I can wait until the actor receives a new message after I complete the promise. Only then the next message is sent. The code for this TestingMailbox is given at the end of the post.
Update: In Akka Typed this can be achieved very elegantly with a BehaviorInterceptor. Just wrap the Behavior under test with a custom interceptor that forwards all messages and signals but lets you observe them.
The mailbox solution for untyped Akka is given below.
The actor can be configured like this:
actorUnderTest = system.actorOf(Props[MyActor]).withMailbox("testing-mailbox"))
I have to make sure the "testing-mailbox" is known by the actor system by providing a configuration:
class MyTest extends TestKit(ActorSystem("some name",
ConfigFactory.parseString("""{
testing-mailbox = {
mailbox-type = "my.package.TestingMailbox"
}
}""")))
with BeforeAndAfterAll // ... and so on
With this being set up, I can change my test like this:
myActor ! Message("A")
val nextMessage = TestingMailbox.nextMessage(actorUnderTest)
promiseFromApiCall.success(Message("B"))
Await.ready(nextMessage, 3.seconds)
myActor ! Message("C")
With a little helper method, I can even write it like this:
myActor ! Message("A")
receiveMessageAfter { promiseFromApiCall.success(Message("B")) }
myActor ! Message("C")
And this is my custom mailbox:
import akka.actor.{ActorRef, ActorSystem}
import akka.dispatch._
import com.typesafe.config.Config
import scala.concurrent.{Future, Promise}
object TestingMailbox {
val promisesByReceiver =
scala.collection.concurrent.TrieMap[ActorRef, Promise[Any]]()
class MessageQueue extends UnboundedMailbox.MessageQueue {
override def enqueue(receiver: ActorRef, handle: Envelope): Unit = {
super.enqueue(receiver, handle)
promisesByReceiver.remove(receiver).foreach(_.success(handle.message))
}
}
def nextMessage(receiver: ActorRef): Future[Any] =
promisesByReceiver.getOrElseUpdate(receiver, Promise[Any]).future
}
class TestingMailbox extends MailboxType
with ProducesMessageQueue[TestingMailbox.MessageQueue] {
import TestingMailbox._
def this(settings: ActorSystem.Settings, config: Config) = this()
final override def create(owner: Option[ActorRef],
system: Option[ActorSystem]) =
new MessageQueue()
}
If it is so important to order messages you should use ask (?) which returns Future and chain them even if you dont expect any response from an actor.
I am running Play with an Akka cluster.
I need an "Singleton Scheduler" to execute some tasks every hour.
What I found out so far is, that I should use ClusterSinglegonManager.
But I am not sure how my Actor must look like.
In my opinion, I wouldn't need a "receive" Method.
That is, how I instatiate my Singleton:
system.actorOf(
ClusterSingletonManager.props(
singletonProps = MySingletonActor.props(configuration),
terminationMessage = PoisonPill,
settings = ClusterSingletonManagerSettings(system)),
name = "mysingletonactor")
That would fit:
object MySingletonActor {
def props(configuration: Configuration): Props = Props(new MySingletonActor(configuration))
}
class MySingletonActor(configuration: Configuration) extends Actor with ActorLogging {
context.system.scheduler.schedule(2 seconds, 2 seconds)(println("Hallo Welt"))
def receive = ???
}
But of course it raises exceptions, because of the missing implementation of the receive method. But it works.
What is the best way to go here?
It feels awkward to just schedule a "Tick" and handle the Tick in the receive Method...
class MySingletonActor(configuration: Configuration) extends Actor with ActorLogging {
case object Tick
context.system.scheduler.schedule(2 seconds, 2 seconds, self, Tick)
def receive = { case Tick => println("Hallo Welt") }
}
Is there any kind of a Singleton Scheduler in Akka?
Instead of writing ??? as receive method, you can use Actor.emptyBehavior to not raise an Exception. This is a Receive-expression that matches no messages at all, ever.
In my Scala application say I have Actor A and Actor B. I want to devise a test case in ScalaTest that would allow me to send a message to Actor A and see what message it sends to Actor B in order to see if A is properly processing it's data and sending the right message to B. How would one test this? It took me a long time to get this cooked up on my own...but it does seem to mostly work.
class A extends Actor { ... }
class B extends Actor { ... }
class C(p: TestProbe) extends B {
override def receive = {
LoggingReceive {
case x =>
println(x.toString)
p.ref ! x
}
}
}
case class MsgToB(...)
// Spec class which extends TestKit
"A" should {
"send the right message to B" {
val p = TestProbe()
val a = TestActorRef[A]
val c = TestActorRef(Props(new C(p)))
// Assume A has a reference to C. Not shown here.
a ! msg
// Assert messages
p.expectMsgType[MsgToB]
}
}
Is this the best means of doing this? Is there a better practice?
To me it sounds like what you want is to test the behaviour of actor A in isolation. In order to do this, you need to be able to control how actor A gets its reference to actor B. For example, you could provide the reference in the actor's constructor:
import akka.actor.{Actor, ActorRef, Props}
class A(refToB: ActorRef) extends Actor { ... }
object A {
def props(refToB: ActorRef): Props = Props(new A(refToB))
}
There are alternative ways you can pass the reference to actor B to actor A, but using the constructor is arguably the easiest choice. In the example above, we also provide a method for creating the correct Props for the actor.
Now that you can control the reference to actor B, you can replace the actor reference with test probe in tests.
import akka.testkit.TestProbe
// Initialise a test probe
val probe = TestProbe()
// Actor A with reference to actor B replaced with the test probe
val a = system.actorOf(A.props(probe.ref))
// Send a message to actor A
a ! someMessage
// Verify that the probe received a correct response from actor A
p.expectMsgType[MsgToB]
Notice that I created the actor using the actor system from the TestKit instead of using the TestActorRef. This means that the actor message processing will be asynchronous instead of synchronous. Personally, I've found the asynchronous testing style to be a better fit because it better represents how the actor is run in a production system. Asynchronous testing is also recommended in the official documentation.
I use IntelliJ IDEA, but the question could relate to other IDEs. There is a great way to navigate the code with Ctrl+click. From the method call it jumps to the method declaration. It really boosts the productivity.
Actor systems are based on message passing. Example in Akka with Scala:
class MyMessage
object MyMessage
class MyActor1 extends Actor {
context.actorOf(Props[MyActor2]) ! MyMessage
}
class MyActor2 extends Actor {
def receive = {
case MyMessage =>
...
}
}
Is there a way to navigate in code between sending the message and receiving the message?
I mean clicking on ! will take me to the definition of ! method in ScalaActorRef, but that's 99% chance that I don't want that. Jumping to the corresponding receive method (or, if possible, to correct case: case MyMessage) would be more appropriate.
How do you navigate the code between actors?
I don't think it is possible in general because an actor can change its behavior at runtime, including what messages it can process - as opposed to methods which can be statically indexed. For example, receive function may be computed depending on the actor state:
class MyActor extends Actor {
var i = 0
def receive = firstReceive
def commonReceive = {
case Increment =>
i += 1
if (i % 3 == 0) context.become(firstReceive)
else context.become(secondReceive)
}
def firstReceive = commonReceive orElse {
case Ping =>
sender ! "zero"
}
def secondReceive = commonReceive orElse {
case Ping =>
sender ! "one or two"
}
}
Now the actor handles messages differently depending on which messages it handled before. And this is only a simple example - actual actor behavior may even be received from the outside!
case class Behavior(receive: Actor.Receive)
class MyActor extends Actor {
def receive = {
case Behavior(r) => context.become(r)
}
}
Another difficulty which is even greater is that you usually have an ActorRef to which you send messages with !. This ActorRef has no static connection with the actor class which contains message handling logic - it is instantiated with Props which can use arbitrary code to determine which actor class should be used:
val r = new Random
val a = actorSystem.actorOf(Props(if (r.nextInt(100) > 50) new FirstActor else new SecondActor))
a ! Message // which handler should this declaration lead to?
This makes finding actual message handler next to impossible.
If you think that it may be worth it to support simpler cases, like the one you provided, you can always submit a feature request to YouTrack.
Not perfect, but what could help would be to use Find Usage (Alt+F7) on the type of the message. For that you probably have to navigate to the type Declaration (Ctrl+Shift+B) first
I wonder if there is an easy way to create a shortcut for the combination.
Another idea would be to use the Structural Search which might be able to find things like excpressions, that match on the class name ...
Once you created a template to your liking you can then record a macro
We have an actor that we are writing unit tests for, and as part of the tests we want to assert that certain messages are sent to another actor in a certain order. In our unit tests, the actor receiving the messages is represented by an Akka TestProbe, which gets injected into the actor under test when it is created.
It is no problem to assert that the messages were sent to the test probe, however we have been struggling to work out a way to assert they are sent in the correct order (we could not find any suitable methods for doing this in the documentation). Any ideas how we achieve this?
Below is a minimal implementation that highlights the problem.
Implementation
case class Message(message: String)
case class ForwardedMessage(message: String)
class ForwardingActor(forwardTo: ActorRef) extends Actor {
def receive = {
case Message(message) =>
forwardTo ! ForwardedMessage(message)
}
}
Unit Test
class ForwardMessagesInOrderTest extends TestKit(ActorSystem("testSystem"))
with WordSpecLike
with MustMatchers {
"A forwarding actor" must {
val forwardingReceiver = TestProbe()
val forwardingActor = system.actorOf(Props(new ForwardingActor(forwardingReceiver.ref)))
"forward messages in the order they are received" in {
forwardingActor ! Message("First message")
forwardingActor ! Message("Second message")
// This is the closest way we have found of achieving what we are looking for, it asserts
// that both messages were received, but doesn't assert correct order. The test will pass
// regardless which way round we put the messages below.
forwardingReceiver.expectMsgAllOf(
ForwardedMessage("Second message"),
ForwardedMessage("First message"))
}
}
}
I'm going to suggest two changes to your test spec. First, when creating the actor under test, use a TestActorRef like so:
val forwardingActor = TestActorRef(new ForwardingActor(forwardingReceiver.ref))
Using a TestActorRef will assure that the CallingThreadDispatcher is used, removing any complications from testing async code (which an actor is). Once you do that, you can change your assertions to:
forwardingReceiver.expectMsg(ForwardedMessage("First message"))
forwardingReceiver.expectMsg(ForwardedMessage("Second message"))
These assertions are inherently In-Order, so if things came in out of this order, they will fail. This should fix your issues.