Testing Akka PubSub mediator with specs2 - scala

I'm fairly new to testing Akka and have been stuck trying to simply verify that a message is sent to a subscriber when a publish message is sent to an Akka mediator from akka.contrib.pattern.DistributedPubSubMediator. I'd like to understand how to get his working before I branch into verifying actual business logic.
import akka.actor.{ ActorRef, Actor }
import akka.contrib.pattern.DistributedPubSubExtension
import akka.contrib.pattern.DistributedPubSubMediator.{ Subscribe, Publish }
import akka.testkit.{ ImplicitSender, TestKit }
import org.specs2.mutable.{ After, Specification }
import queues.{ Publisher, Subscriber }
import scala.concurrent.duration._
class PubSubActorSupport extends TestKit(ActorSystem("PubSubTest")) with ImplicitSender with After {
override def after = system.shutdown()
lazy val subscriberRef = system.actorOf(Props(classOf[Subscriber]))
lazy val publisherRef = system.actorOf(Props(classOf[Publisher]))
lazy val testActorRef = system.actorOf(Props(classOf[MyTestActor]))
}
class MyTestActor extends Actor {
def receive: Receive = { case x => () }
}
object EventsTest extends Specification {
"Publish/Subscriber Actors" should {
"work" in new PubSubActorSupport {
subscriberRef ! ("topic", testActorRef)
publisherRef ! ("topic", "Hello!")
// assert that MyTestActor received "Hello!" from the publisher actor
expectMsg(500 milli, "Hello!")
}
}
}
I figure I'm not using expectMsg correctly. My Subscriber/Publisher actors are below.
trait Mediator extends Actor {
val mediator = DistributedPubSubExtension(context.system).mediator
}
class Publisher extends Mediator {
def receive = {
case (topic: String, msg: Any) => mediator ! Publish(topic, msg)
}
}
class Subscriber extends Mediator {
def receive = {
case (topic: String, ref: ActorRef) => mediator ! Subscribe(topic, ref)
}
}

Check this sample for testing SubPub pattern
https://github.com/JAVEO/clustered-chat/blob/master/test/actors/UserSocketSpec.scala
expectMsg just verify message that is send to "sender". And if you want to verify a message sent to a subscribers, write like this (From the sample above)
val mediator = DistributedPubSubExtension(system).mediator
val chatMember1 = TestProbe()
mediator ! Subscribe(topic, chatMember1.ref)
//... Call the publisher to publish message
chatMember1.expectMsg(ChatMessage(UserId, message))

Related

Testing actor crash using TestActorRef

I am new to actors, I am learning, how to test actors using TestActorRef
My actor code:
package actors
import actors.GreetingActor.Hi
import akka.actor.Actor
object GreetingActor {
case class Hi()
}
class GreetingActor extends Actor {
var greeting = ""
override def receive: Receive = {
case Hi() =>
greeting = "Hi"
case _ =>
throw new IllegalArgumentException("not supported message")
}
override def postRestart(reason: Throwable) = {
println(s"actor is restarted because of ${reason.getMessage}")
}
}
I am sure that everything works as I want in this code, but I can't show it in test. Especially I can't show that one of the most important thing, that my actor crashed. The test is very simple and obvious I send message that is not Hi() and should track somehow that actor crashed with IllegalArgumentException. My current test code:
package actors
import actors.GreetingActor.Hi
import akka.actor.ActorSystem
import akka.testkit.{TestActorRef, TestKit}
import org.scalatest.{MustMatchers, WordSpecLike}
class GreetingActorTest extends TestKit(ActorSystem("testsystem")) with WordSpecLike
with MustMatchers with StopSystemAfterAll {
"A Hello Actor" must {
"change state when it receives a message, single threaded" in {
val greetingActor = TestActorRef[GreetingActor]
greetingActor ! Hi()
greetingActor.underlyingActor.greeting mustBe "Hi"
}
"throw exception when it received unknown message, single threaded" in {
val greetingActor = TestActorRef[GreetingActor]
greetingActor ! "hi"
//some code that checks that actor crashed
}
}
}
The question is how can I track in test that my actor crashed using TestActorRef? Appreciate any help.
Change your test to the following:
"throw exception when it received unknown message, single threaded" in {
assertThrows[IllegalArgumentException] {
val greetingActor = TestActorRef[GreetingActor]
greetingActor.receive("hi")
}
}
Per the actor docs, you need to use receive so the exception doesn't get swallowed:
http://doc.akka.io/docs/akka/current/scala/testing.html#The_Way_In-Between__Expecting_Exceptions

neat way to test become unbecome swtichover in scala

I am trying to test state switch over of receive method.
Found this stackoverflow post, but that also not clearly give a solution.
Please find below simplified code snippet :-
package become_unbecome_basics
import akka.actor.{Actor, ActorSystem}
import akka.testkit.{ImplicitSender, TestActorRef, TestKit}
import become_unbecome_basics.BasicBecomeUnbecomeActor.{SWITCH_TO_MASTER, SWITCH_TO_STANDBY}
import com.typesafe.scalalogging.LazyLogging
import org.scalatest.FlatSpecLike
import org.scalatest.Matchers._
class BecomUnbecomeSwitchoverTest extends TestKit(ActorSystem("testSystem")) with ImplicitSender with FlatSpecLike{
"initially receive" should "points to master" in {
val aRef = TestActorRef[BasicBecomeUnbecomeActor]
val actor = aRef.underlyingActor
//not sure, weather we have something like this to assert
//actor.receive should be(actor.master)
}
}
object BasicBecomeUnbecomeActor{
case object SWITCH_TO_MASTER
case object SWITCH_TO_STANDBY
}
class BasicBecomeUnbecomeActor extends Actor with LazyLogging{
override def receive: Receive = master
def master: Receive = {
case SWITCH_TO_STANDBY =>
context.become(standBy)
case msg => logger.debug(s"master : $msg received")
}
def standBy: Receive = {
case SWITCH_TO_MASTER =>
context.unbecome()
case msg => logger.debug(s"standBy : $msg received")
}
}
The StackOverflow post you mentioned contains two suggested ways to test your actor.
Emit the state changes to some other actor.
Don't test state change, but the actor's behaviour.
In the first example, you would have some way of sending information out of your actor on every state change. In Akka, sending state change information as actor messages is natural way to implement this.
import akka.actor._
import akka.testkit._
class ExampleActor(notify: ActorRef) extends Actor with ActorLogging {
import ExampleActor.{Master, StandBy}
def receive: Receive = master
def master: Receive = {
case StandBy =>
notify ! StandBy
context.become(standby)
case msg =>
log.debug("received msg in master: {}", msg)
}
def standby: Receive = {
case Master =>
notify ! Master
context.become(master)
case msg =>
log.debug("received msg in standby: {}", msg)
}
}
object ExampleActor {
def props(notify: ActorRef): Props = Props(new ExampleActor(notify))
case object Master
case object StandBy
}
class ExampleActorTest extends TestKit(ActorSystem("testSystem")) with FlatSpecLike {
"ExampleActor" should "move to stand by state" in {
val probe = TestProbe()
val actor = system.actorOf(ExampleActor.props(probe.ref))
actor ! ExampleActor.StandBy
probe.expectMsg(ExampleActor.StandBy)
}
}
(I haven't run the code yet so apologies for any errors in the code)
In the code above, the ExampleActor is a stateful actor which notifies the given actor reference of any state changes. Note that this doesn't allow inspecting the current state, but instead a log of state transitions. Also, it is possible to introduce a bug in the state notification code because the notification code is manually added to the actor instead of the actor doing it automatically.
I changed the testing style to asynchronous testing style to get more realistic tests.
The state change notifications allows you to get information about what specific state the actor transitions to, but it doesn't tell you if it works the way it should. Instead of testing what state changes the actor goes through, how about testing what the actor itself does.
class Accumulator extends Actor with ActorLogging {
import Accumulator._
def receive: Receive = accumulatorReceive(0)
def accumulatorReceive(x: Int): Receive = {
case Add(i) => next(x + i)
case Remove(i) => next(x - i)
case Multiply(i) => next(x * i)
case Divide(i) => next(x / i)
case Get => sender() ! x
}
def next(x: Int) = context.become(accumulatorReceive(x))
}
object Accumulator {
def props: Props = Props(new Accumulator)
case class Add(i: Int)
case class Remove(i: Int)
case class Multiply(i: Int)
case class Divide(i: Int)
case object Get
}
class AccumulatorTest extends TestKit(ActorSystem("testSystem")) with FlatSpecLike {
import Accumulator._
"Accumulator" should "accumulate" in {
val probe = TestProbe()
val actor = system.actorOf(Accumulator.props)
actor ! Add(3)
actor ! Remove(1)
actor ! Multiply(4)
actor ! Divide(2)
probe.send(actor, Get)
probe.expectMsg(5)
}
}
In this example, Accumulator does state changes, but it doesn't notify when its state has changed. Instead, it has a specific get command for inspecting interesting parts about its state. In the test, we send multiple messages that cause state changes in the accumulator actor. Finally, we inspect the result of these messages by querying the accumulator.

Testing Spray API with Akka Router

I'm working on a Spray API, with an Akka router to send the incoming messages on to a pool of actors for handling the logic. Now I want to write some tests for the API, but I'm struggling to find the right structure for the code. The API looks as follows at the moment:
import akka.actor.{ActorRef, ActorSystem, Props, Actor}
import akka.io.IO
import akka.routing.SmallestMailboxPool
import akka.util.Timeout
import akka.pattern.ask
import com.typesafe.config.ConfigFactory
import spray.json._
import spray.can.Http
import scala.concurrent.duration._
import spray.routing._
import spray.http._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.Success
import scala.util.Failure
object implicits{
implicit val system = ActorSystem("ApiSystem")
implicit val timeout = Timeout(5.seconds)
implicit val conf = ConfigFactory.load()
// Custom case class for parsing JSON parameter.
case class Msg(key1:String, key2:String, key3:Int)
object JsonProtocol extends DefaultJsonProtocol {
implicit val msg = jsonFormat3(Msg)
}
case class PostMsg(msg:String)
case object PostSuccess
case class PostFailure(msg:String)
}
import implicits._
object MyApi extends App {
override def main(Args: Array[String]):Unit = {
// create and start our service actor
val service = system.actorOf(Props(new MyApiActor(system)), "MyApi-service")
IO(Http) ? Http.Bind(service, interface = conf.getString("http.host"), port = conf.getInt("http.port"))
}
}
class MyApiActor(system: ActorSystem) extends Actor with MyApiService {
// the HttpService trait defines only one abstract member, which
// connects the services environment to the enclosing actor or test
def actorRefFactory = context
// this actor only runs our route, but you could add
// other things here, like request stream processing
// or timeout handling
def receive = runRoute(myRoute)
}
// this trait defines our service behavior independently from the service actor
trait MyApiService extends HttpService {
import implicits.JsonProtocol._
var actorPool = system.actorOf(SmallestMailboxPool(conf.getInt("actor-number")).props(Props(new HandlingActor(conf))), "msgRouter")
val myRoute =
path("msg") {
post {
entity(as[String]) { obj =>
try{
// if this parsing succeeds, the posted msg satisfies the preconditions set.
obj.parseJson.convertTo[Msg]
} catch {
case e: DeserializationException => {
complete(HttpResponse(status=StatusCodes.BadRequest, entity="Invalid json provided."))
}
case e: Exception => {
complete(HttpResponse(status=StatusCodes.InternalServerError, entity="Unknown internal server error."))
}
}
onComplete(actorPool ? PostMsg(obj)) {
case Success(value) => complete(HttpResponse(status = StatusCodes.OK, entity = "Pushed Msg"))
case Failure(value) => complete(HttpResponse(status = StatusCodes.InternalServerError, entity = "Handling failed."))
}
}
}
}
}
What I would like to test is the response of the API to various HTTP messages (i.e. correct calls, incorrect calls etc.). The logic in the handling actor is simply to push the message to a Kafka bus, so I would like to "mock" this behaviour (i.e. be able to test the API response if this push succeeds and also what happens when this push fails).
The thing I'm struggling with most at the moment is how to setup the test. For now, I am setting up the API using the same commands as in the main method shown, but I need to specify a different actorPool, as I don't want any messages to actually be pushed. How should I best go about achieving such tests?
I am using Scalatest, with the Akka and Spray testkit. (plus possibly mockito for mocking if necessary)
I have few suggestions to make your testing easier:
Do not create the actor pool in your trait. Instead inject the ActorRef from the ActorPool using a def instead of a val in the route. Then it will be easier to inject your actorPool TestProbe() to test. For example (I have not tried/compiled this code):
class MyApiActor extends Actor with MyApiService {
// the HttpService trait defines only one abstract member, which
// connects the services environment to the enclosing actor or test
def actorRefFactory = context
val actorPool = context.actorOf(SmallestMailboxPool(conf.getInt("actor-number")).props(Props(new HandlingActor(conf))), "msgRouter")
// this actor only runs our route, but you could add
// other things here, like request stream processing
// or timeout handling
def receive = runRoute(myRoute(actorPool))
}
// this trait defines our service behavior independently from the service actor
trait MyApiService extends HttpService {
import implicits.JsonProtocol._
def myRoute(actorPool: ActorRef) =
path("msg") {
post {
entity(as[String]) { obj =>
try{
// if this parsing succeeds, the posted msg satisfies the preconditions set.
obj.parseJson.convertTo[Msg]
} catch {
case e: DeserializationException => {
complete(StatusCodes.BadRequest, "Invalid json provided.")
}
case e: Exception => {
complete(StatusCodes.InternalServerError, "Unknown internal server error.")
}
}
onComplete(actorPool ? PostMsg(obj)) {
case Success(value) => complete(StatusCodes.OK, "Pushed Msg")
case Failure(value) => complete(StatusCodes.InternalServerError, "Handling failed.")
}
}
}
}
}
Then the test can look like this:
class HttpListenerSpec extends WordSpecLike with Matchers with ScalatestRouteTest with MyApiService {
"An HttpListener" should {
"accept GET at /msg" in {
val actorPool = TestProbe()
(stuff for responding with TestProbe()...)
Get("/msg") ~> myRoute(actorPool.ref) ~> check {
status shouldBe OK
val response = responseAs[String]
assert(...)
}
}
}
}
Also, as a final suggestion. There are implicit conversions that integrate spray json and spray so you can do entity(as[Msg]). Look for the following:
import spray.httpx.marshalling._
import spray.httpx.unmarshalling._
import spray.httpx.SprayJsonSupport._
import MsgJsonProtocol._

Testing Akka Typed behavior

How would I test that a given behavior sends the messages I expect?
Say, three messages of some type, one after the other...
With regular actors (untyped) there was the TestProbe from regular Akka with methods like expectedMsg:
http://doc.akka.io/api/akka/current/index.html#akka.testkit.TestProbe
With akka-typed I'm scratching my head still. There is something called EffectfulActorContext, but I've no idea how to use that.
Example
Say I am writing a simple PingPong service, that given a number n replies with Pong(n) n-times. So:
-> Ping(2)
Pong(2)
Pong(2)
-> Ping(0)
# nothing
-> Ping(1)
Pong(1)
Here is how this behavior might look:
case class Ping(i: Int, replyTo: ActorRef[Pong])
case class Pong(i: Int)
val pingPong: Behavior[Ping] = {
Static {
case Ping(i, replyTo) => (0 until i.max(0)).map(_=> replyTo ! Pong(i))
}
}
My Hack
Now since I can't figure out how to make this work, the "hack" that I am doing right now is making the actor always reply with a list of responses. So the behavior is:
case class Ping(i: Int, replyTo: ActorRef[List[Pong]])
case class Pong(i: Int)
val pingPong: Behavior[Ping] = {
Static {
case Ping(i, replyTo) => replyTo ! (0 until i.max(0)).map(_=>Pong(i)).toList
}
}
Given this hacky change, the tester is easy to write:
package com.test
import akka.typed.AskPattern._
import akka.typed.ScalaDSL._
import akka.typed.{ActorRef, ActorSystem, Behavior, Props}
import akka.util.Timeout
import com.test.PingPong.{Ping, Pong}
import org.scalatest.{FlatSpec, Matchers}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.concurrent.{Await, Future}
object PingPongTester {
/* Expect that the given messages arrived in order */
def expectMsgs(i: Int, msgs: List[Pong]) = {
implicit val timeout: Timeout = 5 seconds
val pingPongBe: ActorSystem[Ping] = ActorSystem("pingPongTester", Props(PingPong.pingPong))
val futures: Future[List[Pong]] = pingPongBe ? (Ping(i, _))
for {
pongs <- futures
done <- {
for ((actual, expected) <- pongs.zip(msgs)) {
assert(actual == expected, s"Expected $expected, but received $actual")
}
assert(pongs.size == msgs.size, s"Expected ${msgs.size} messages, but received ${pongs.size}")
pingPongBe.terminate
}
} Await.ready(pingPongBe.whenTerminated, 5 seconds)
}
}
object PingPong {
case class Ping(i: Int, replyTo: ActorRef[List[Pong]])
case class Pong(i: Int)
val pingPong: Behavior[Ping] = {
Static {
case Ping(i, replyTo) => replyTo ! (0 until i.max(0)).map(_=>Pong(i)).toList
}
}
}
class MainSpec extends FlatSpec with Matchers {
"PingPong" should "reply with empty when Pinged with zero" in {
PingPongTester.expectMsgs(0, List.empty)
}
it should "reply once when Pinged with one" in {
PingPongTester.expectMsgs(1, List(Pong(1)))
}
it should "reply with empty when Pinged with negative" in {
PingPongTester.expectMsgs(-1, List.empty)
}
it should "reply with as many pongs as Ping requested" in {
PingPongTester.expectMsgs(5, List(Pong(5), Pong(5), Pong(5), Pong(5), Pong(5)))
}
}
I'm using EffectfulActorContext for testing my Akka typed actors and here is an untested example based on your question.
Note: I'm also using the guardianactor provided in the Akka-typed test cases.
class Test extends TypedSpec{
val system = ActorSystem("actor-system", Props(guardian()))
val ctx: EffectfulActorContext[Ping] = new EffectfulActorContext[Ping]("ping", Ping.props(), system)
//This will send the command to Ping Actor
ctx.run(Ping)
//This should get you the inbox of the Pong created inside the Ping actor.
val pongInbox = ctx.getInbox("pong")
assert(pongInbox.hasMessages)
val pongMessages = pongInbox.receiveAll()
pongMessages.size should be(1) //1 or whatever number of messages you expect
}
Edit (Some more info): Cases where I need to add a replyTo ActorRef in my messages I do the following:
case class Pong(replyTo: ActorRef[Response])
val responseInbox: SyncInbox[Response] = Inbox.sync[Response]("responseInbox")
Pong(responseInbox.ref)
My initial approach to testing was to extend Behavior class
class TestQueueBehavior[Protocol] extends Behavior[Protocol] {
val messages: BlockingQueue[Protocol] = new LinkedBlockingQueue[Protocol]()
val behavior: Protocol => Unit = {
(p: Protocol) => messages.put(p)
}
def pollMessage(timeout: FiniteDuration = 3.seconds): Protocol = {
messages.poll(timeout.toMillis, TimeUnit.MILLISECONDS)
}
override def management(ctx: ActorContext[Protocol], msg: Signal): Behavior[Protocol] = msg match {
case _ ⇒ ScalaDSL.Unhandled
}
override def message(ctx: ActorContext[Protocol], msg: Protocol): Behavior[Protocol] = msg match {
case p =>
behavior(p)
Same
}
}
then I could call behavior.pollMessage(2.seconds) shouldBe somethingToCompareTo which was very similar to using TestProbe.
Although I think EffectfulActorContext is the right way to go, unfortunately couldn't figure out how to properly use it.

Test response from child on akka

I'm new to using scala on the akka system.
I have a parent actor (named "manager") which sends a message to a child, and receives an answer, which it forwards to next child.
The final actor saves the result of the whole process.
I want to create an end to end test which sends a message to the parent ("manager"), and expect a message from the last actor which recieves the result.
I'm looking for a simple way to direct the final actor to send a message back to the test. Since the test is not the sender, and it's not a simple actor, i don't know how to direct the message correctly.
Following is code of the test:
class EndToEndTest extends TestKit(ActorSystem("MyActorSystem"))
with FunSuiteLike with Matchers with BeforeAndAfterAll with ImplicitSender {
override def afterAll {
TestKit.shutdownActorSystem(system)
}
test("should send result") {
val actor = system.actorOf(Props(new Manager(name = "ActorManager")))
actor ! tPoint(1000L)
actor ! tPoint(2000L)
expectMsg(ActorResult(1))
}
}
and of the last child actor:
class SensorHealthReportMySqlActor extends Actor with ActorLogging {
def receive = {
case Result(result: Long) =>
//this is where i would like to send the test a message with Result(result)
case _ =>
log.error("Unknown message type")
}
}
Any help would be appreciated.
I think the solution you want is to pass an ActorRef as a parameter in a message so the receiver knows where to send the reply when the message should be sent to some actor other than the sender of the current message.
So something along these lines:
import akka.actor.{Actor, Props, ActorRef, ActorLogging}
case class tPoint(n: Long)
case class Result(result: Long, replyTo: ActorRef)
case class ActorResult(result: Long)
class Manager(name: String) extends Actor {
val child = context.actorOf(Props(new SensorHealthReportMySqlActor))
override def receive: Receive = {
case msg: tPoint ⇒
child ! Result(msg.n, sender())
}
}
class SensorHealthReportMySqlActor extends Actor with ActorLogging {
def receive = {
case Result(result: Long, replyTo: ActorRef) =>
//this is where i would like to send the test a message with Result(result)
replyTo ! ActorResult(1)
case _ =>
log.error("Unknown message type")
}
}
In this case the sender() in the Manager receive method is the test itself, which is an ImplicitSender (as you declared in your test). This is an actor created behind the scenes which can receive messages.
In a non-test system, you can use the ask pattern or an Inbox for this same purpose. See the documentation at Actors.