Tomasz Lewowski
Tomasz Lewowski

Reputation: 1965

Strict mode for Akka TestKit probes

I am writing a test case for an actor-based application. One of the components can be roughly defined as follows:

class MyActor(a: ActorRef, b: ActorRef) extends Actor {
    override def receive: Receive = {
        case _ => 
            a ! "Got message!"
            b ! "Hello!"
    }
}

now, to write a test case I'm using akka-testkit and TestProbe. An important part of test case looks as follows:

val a = TestProbe()
val b = TestProbe()
val c = system.actorOf(Props(new MyActor(a.testActor, b.testActor)))

c ! "Message!"
a.expectMsg("Got message!")

Now the problem is that the test case passes, even though the message sent to b was not expected and thus not validated.

I am aware that I can call b.expectNoMsg() in the beginning of the test case which would take care of this specific problem, but somehow I believe that this is not really a scalable approach (I would have to add it each time after all expected calls, which is quite cumbersome).

So my question is: is there an option to run akka-testkit in a strict mode, so that every message will have to be somehow expected? Preferable way is via TestKit, ActorSystem or TestProbe configuration, but any solution which would not require modifying each test case is fine (so invoking expectNoMsg() in the end of each communication is not a solution)

Upvotes: 1

Views: 107

Answers (1)

johanandren
johanandren

Reputation: 11479

For something to fail the test it would have to throw an assertion error of some kind of the thread that executes the test, but as the probe is asynchronous any failure detection you put in it will happen on a different thread, therefore there needs to be a place where you call a method from the test (just like expectNoMsg()).

That being said, you could still abstract over that concept, for example hooking into your testing toolkit.

One possible way to do that would be using a higher order function:

def failOnUnexpectedMessage[T](test: ActorRef => T): T = {
  val probe = TestProbe()
  val result = test(probe.ref)
  probe.expectNoMsg()
  result
}

You could then use that in your tests (Scalatest word spec style here):

"My actor" should {
  "something something" in failOnUnexpectedMessage { ref => 
    val actor = ...construct and pass it ref as b...
    ...rest of the test...
  }
}

Upvotes: 2

Related Questions