Testing actor crash using TestActorRef - scala

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

Related

In Akka, how do I manage blocking method calls, like reading from StdIn?

I'm beginning to using Scala and the AKKA pattern, and i have wrote this code, but it doesn't work and i don't know why...
I have created a little project that read user input from the console.
when this user have wrote a 'keyword', the keyWord Actor (Child) will interpret it and will communicate with the console Actor (Grand parent).
the action Actor will be use to broadcast and do some more stuff.
When i enter the command 'rename' in the console Actor, i enter into the action Actor and after that in the keyWord Actor and enter in the Rename Method, but after that nothing, i didn't enter into the rename method on the console Actor.
Can you help me ?
If you saw any wrong pratice, please don't hesite to tell me how to resolve that :).
Thank you !
Main
import ConsoleActor._
import akka.actor.ActorSystem
object Main extends App {
val system = ActorSystem("My-Little-IRC")
val consoleActor = system.actorOf(ConsoleActor.props, "consoleActor")
consoleActor ! ReadConsoleInput
system.terminate()
}
consoleActor
import ActionActor.TreatInputUser
import akka.actor.{Actor, Props}
import scala.io.StdIn
object ConsoleActor {
case class ReadConsoleInput()
case class StopLoop()
case class Rename()
case class WhoIAm()
def props = Props[ConsoleActor]
}
class ConsoleActor() extends Actor {
val keyWordActor = context.actorOf(KeyWordActor.props(this.self), "keyWordActor")
val actionActor = context.actorOf(ActionActor.props(keyWordActor), "actionActor")
var currentUser: String = ""
var loop: Boolean = true;
import ConsoleActor._
def isValidString( str: String ): Boolean = {
var isValid: Boolean = false
if (str != null && !str.trim().isEmpty)
isValid = true
isValid
}
def initiatePresentation( ) = {
println("Hi ! Who are you ?")
currentUser = StdIn.readLine()
println(s"Nice to meet you ${currentUser}, I'm your console app !")
}
def receive = {
case ReadConsoleInput => {
initiatePresentation
var value = ""
while (loop) {
println("Yes ?")
value = StdIn.readLine()
if (isValidString(value)) {
actionActor ! TreatInputUser(value)
}
}
}
case StopLoop => {
println("stop Loooop !")
loop = false
}
case Rename => {
println(s"${currentUser} was a good name ! Which is your new name ?")
currentUser = StdIn.readLine()
println(s"Nice to meet you -again- ${currentUser}")
}
case WhoIAm =>{
println(s"I'm ${currentUser}")
}
}
}
actionActor
import ActionActor.TreatInputUser
import akka.actor.{Actor, ActorRef, Props}
import akka.util.Timeout
import scala.concurrent.duration._
import akka.pattern.ask
import scala.concurrent.Await
object ActionActor {
case class TreatInputUser(string: String)
def props(keyWordActor: ActorRef) = Props(new ActionActor(keyWordActor))
}
class ActionActor(keyWordActor: ActorRef) extends Actor {
import KeyWordActor._
def receive = {
case TreatInputUser(string) => {
implicit val timeout = Timeout(5 seconds)
var isKeyWord = keyWordActor ? IsKeyWord(string)
val isKeyWordResult = Await.result(isKeyWord, timeout.duration).asInstanceOf[ Boolean ]
println(isKeyWordResult)
if (isKeyWordResult) {
keyWordActor ! HandleKeyWord(string)
}
else {
println("bla bla bla")
}
}
}
}
keyWord actor
import ConsoleActor.{Rename, StopLoop, WhoIAm}
import akka.actor.{Actor, ActorRef, Props}
object KeyWordActor {
case class IsKeyWord(key : String)
case class HandleKeyWord(key : String)
def props(consoleActor: ActorRef) = Props(new KeyWordActor(consoleActor))
}
class KeyWordActor(consoleActor: ActorRef) extends Actor {
import KeyWordActor._
val KeysWord : Map[ String, ()=> Any] = Map("rename" -> renameFunction, "stop" -> stopFunction, "42" -> uselessfunction, "john doe ?" -> AmIJohnDoe)
def renameFunction() = {
println("here")
consoleActor ! Rename
}
def stopFunction() = {
consoleActor ! StopLoop
}
def uselessfunction() = {
println("useless")
}
def AmIJohnDoe() ={
consoleActor ! WhoIAm
}
def receive = {
case IsKeyWord(key) => {
sender ! KeysWord.contains(key.toLowerCase)
}
case HandleKeyWord(key) => {
if (KeysWord.contains(key.toLowerCase)) {
KeysWord(key.toLowerCase)()
}
}
}
}
You must not block in the receive method. The way you wrote it (with a while loop), the initial ReadConsoleInput message never finishes processing, and any subsequent messages (like StopLoop) will sit untouched in the Actor mailbox forever.
If you must selectively read from StdIn (as opposed to just continuously reading in e.g. your Main class) then one approach could be to change your ConsoleActor so that when it receives a ReadConsoleInput message, it should just try to do StdIn.readLine once, and forward the result to the ActionActor. Since the StdIn.readLine call itself is also blocking, you should do it asynchronously. The "pipe" pattern comes in handy here:
import akka.pattern.pipe
import scala.concurrent.Future
//...
def receive = {
case ReadConsoleInput =>
import context.dispatcher //provide a thread pool to do async work
Future(StdIn.readLine()) //read a line of input asynchronously
.filter(isValidString) //only continue if line is valid
.map(TreatInputUser) //wrap the (valid) String in a message
.pipeTo(actionActor) //forward it
case Rename => ...
}
This way, the ConsoleActor immediately becomes available again to process new messages, while your ActionActor will receive a TreatInputUser message whenever the user finishes typing a line in the console.
You can apply the same pattern inside your ActionActor, instead of relying on Await.
If you want to close the loop so you can continue sending messages, I'd use behaviour to ensure that two StdIn.readLine calls are not interfering.

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._

Handling exceptions in preStart in actors

I have a service that has a supervisor responsible for build children actors and handling their exceptions.
ServiceMain -> Supervisor -> DiscoveryActor
To build the DiscoveryActor I call the following
Await.result(supervisor ? (Props[Discovery], "Discovery"), Duration.create(60, SECONDS)) match {
case actor: ActorRef =>
discoveryActor = actor
case ex: Exception =>
logger.error("Failed to initialize discovery actor", ex)
sys.exit(1)
}
Which gets handled by this code in Supervisor
def receive = {
case p: Props => sender() ! context.actorOf(p)
case (p: Props, s: String) => sender() ! context.actorOf(p, s)
}
It is expected of the DiscoveryActor to throw an exception in preStart() if it can't research a host as configured. The exception ApiConnectionException is thrown by the actor and is captured by akka and turned into a ActorInitializationException.
I have tired catching this exception in the Await and with the superviseStrategy as below
override val supervisorStrategy =
AllForOneStrategy() {
case _: Exception => Escalate
}
But neither of these have managed to catch it, what I am attempting to do is catch such exception and exit the application.
If anyone can point out where I am going wrong or what I have missed then I shall be very grateful!
I simplified your code a bit just to get straight to the root of the problem. You can copy it and paste it into your editor. It uses ScalaTest suite.
SupervisorStrategy defined in Supervisor actor does catch an exception thrown by Discovery actor in preStart method. You might want to have a closer look at your own code.
Your Await block is trying to catch an exception, but it's not possible in this context. The Exception is thrown by Discovery actor, not send as a message. Ask pattern (?) you use simply awaits for a message to arrive though. Only use of SupervisorStrategy can get you back a thrown exception. Instead of escalating exception in your Supervisor you could send a message to your application Guardian actor saying that initialization failed so the application should exit. Or do it directly in your Supervisor.
import java.util.concurrent.TimeUnit
import akka.actor.SupervisorStrategy.Escalate
import akka.actor._
import akka.pattern.ask
import akka.testkit.{ImplicitSender, TestKit}
import akka.util.Timeout
import org.scalatest.{BeforeAndAfterAll, FunSuiteLike, Matchers}
import scala.concurrent.Await
abstract class ActorSuite(systemName: String)
extends TestKit(ActorSystem(systemName))
with FunSuiteLike
with ImplicitSender
with Matchers
with BeforeAndAfterAll {
override def afterAll {
TestKit.shutdownActorSystem(system)
}
}
class FailingActorInitializationSuite extends ActorSuite("failing-system") {
test("run it") {
val supervisor = system.actorOf(Props[Supervisor])
var discoveryActor: ActorRef = null
implicit val timeout = Timeout(60, TimeUnit.SECONDS)
Await.result(
supervisor ?(Props[Discovery], "Discovery"), timeout.duration) match {
case actor: ActorRef =>
discoveryActor = actor
}
}
}
class Supervisor extends Actor with ActorLogging {
override val supervisorStrategy =
AllForOneStrategy() {
case e: Exception =>
log.error(s"Caught an exception [${e.getCause.getMessage}] and escalating")
Escalate
}
override def receive: Receive = {
case (p: Props, s: String) => sender() ! context.actorOf(p, s)
}
}
class Discovery extends Actor {
override def preStart(): Unit = {
super.preStart()
throw new RuntimeException("Can't create")
}
override def receive: Actor.Receive = {
case _ =>
}
}

Testing Akka PubSub mediator with specs2

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))

Instantiation in AKKA Microkernel

Having followed the documentation example (2.1.4), I was having trouble with a Microkernel loaded actor processing messages, where the Bootable extension class is defined as follows:
class HelloKernel extends Bootable {
val system = ActorSystem("hellokernel")
def startup = {
system.actorOf(Props[HelloActor]) ! Start
}
def shutdown = {
system.shutdown()
}
}
If a dummy (i.e. not used anywhere else in the code) instance is created, as shown below, messages are then processed as expected.
class HelloKernel extends Bootable {
val system = ActorSystem("hellokernel")
val dummyActor = system.actorOf(Props[HelloActor])
def startup = {
system.actorOf(Props[HelloActor]) ! Start
}
def shutdown = {
system.shutdown()
}
}
Should there indeed be a dummy instantiation or, by doing it, am I causing some side effect, resulting in messages being processed?
Based on the code given by Thomas Letschert in Akka 2.1 minimal remote actor example I have turned the server side into a Microkernel hosted actor.
import akka.actor.Actor
import akka.actor.ActorLogging
import akka.actor.ActorSystem
import akka.actor.Props
import akka.kernel.Bootable
class Joe extends Actor {
def receive = {
case msg: String => println("joe received " + msg + " from " + sender)
case _ => println("Received unknown msg ")
}
}
class GreetServerKernel extends Bootable {
val system = ActorSystem("GreetingSystem")
val joe = system.actorOf(Props[Joe], name = "joe")
println(joe.path)
joe ! "local msg!"
println("Server ready")
def startup = {
}
def shutdown = {
println("PrimeWorker: Shutting Down")
system.shutdown
}
}
In this case the dummy instantiation, which when removed messages are not processed, is
val joe = system.actorOf(Props[Joe], name = "joe")
The caller code is
import akka.actor._
import akka.actor.ActorDSL._
object GreetSender extends App {
implicit val system = ActorSystem("GreetingSystem")
val joe = system.actorFor("akka://GreetingSystem#127.0.0.1:2554/user/joe")
println(joe.path)
val a = actor(new Act {
whenStarting { joe ! "Hello Joe from remote" }
})
joe ! "Hello"
println("Client has sent Hello to joe")
}
If the code you posted is indeed accurate, then just move the instantion of the joe instance into the startup operation instead of in the constructor for the bootable class:
def startup = {
system.actorOf(Props[Joe], name = "joe")
}
The actor tied to the name joe needs to have been started up before someone can look it up by name and send messages to it. It's basically the same thing as starting it up in the constructor of the bootable class, but I believe that convention dictates to do all actor instantiation in the startup function as opposed to the bootable class body (and thus the constructor)