I am trying to understand the akka-Testkit", and hope it is ok to ask about it.
I found some tutorials and blogs that either access a state- or a lastMsg- attribute on the underlyingActor on the TestActorRef. However, a TestActorRef from the the "akka-testkit_2.11" % "2.4.10" does not have these attributes. I looked at the example on the akka website, and maybe I am missing something, but they show testing of among other an echo actor, but not with any simple actor implementations.
So, could someone help me understand how to test a worker that will respond with the same number if n % 3 == 0 (which is the case in the example). I would prefer not to use a future and the ask pattern if possible, and would like to make a test on the response that the actor will give (from that actors perspective by accessing its state or something similar).
class ProjectEulerScalaTestAkka extends TestKit(ActorSystem("testing")) with WordSpecLike with MustMatchers {
"A simple actor" must {
val actorRef = TestActorRef[Worker]
"receive messages" in {
actorRef ! 3
actorRef.underlyingActor.state//must not equal("world")
}
}
}
related:
How do I test an Akka actor that sends a message to another actor?
For now I am using a synchronized testing approach;
import akka.actor.ActorSystem
import akka.testkit.{TestActorRef, TestKit}
import org.scalatest.Matchers
import org.scalatest.WordSpecLike
import akka.pattern.ask
import scala.concurrent.Future
import scala.concurrent.duration._
import scala.util.Success
class ProjectEulerScalaTestAkka extends TestKit(ActorSystem("testing")) with WordSpecLike with Matchers {
implicit val time = akka.util.Timeout(100 seconds)
"A simple actor" must {
val actorRef = TestActorRef[Worker]
"receive messages" in {
val f = (actorRef ? 3).asInstanceOf[Future[Int]]
val reply = f.value.get
reply should equal (Success(3))
}
}
}
What I did was mock the interface of the Actor that I was sending the message to, capturing the message and sending back a success message to the testActor reference.
success object with the captured message payload
case class SuccessWith(capturedMessage:Any = null)
a mock actor that you send your message to which, in turn, returns some value to the test actor
case class MockActor(requester: ActorRef) extends Actor {
override def receive: Receive = {
case i: Int => {
requester ! i
}
}
}
set up the actor you're wanting to unit test
val actorRef = system.actorOf(Props(new YourActor(args)))
and then your test
"A simple actor" must {
"receive messages" in {
val f = actorRef ! 3
expectMsg(Success(3))
}
}
}
Related
I am pretty new to the Akka world and I have to migrate a project from Spray to akka-http.
In spray, a route was of type Route = RequestContext ⇒ Unit.
But in akka-http, it is of type Route = RequestContext ⇒ Future[RouteResult].
So in spray, we would often handle and complete our requests through a chain of Actors (outside of the main controller) using only fire-and-forget so we didn't have to "ask" and the performance was great. Now, we have to use "ask" every time we pass the request to another Actor (correct me if I'm wrong)
I've been searching a lot and I found a few options which I'm not sure if they fully satisfy me (the need to complete a request in another Actor without the need to return it back to the controller). So that's where you could help me :)
Option 1: Using onSuccess/onComplete
Does using this block my main controller from receiving more requests?
Option 2: Using Futures and using RouteResult.Complete
I've found the following example at How to complete a request in another actor when using akka-http:
import akka.actor.{ActorSystem, Actor, Props}
import akka.pattern.ask
import akka.stream.ActorMaterializer
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.{RequestContext, RouteResult}
import scala.concurrent.Future
import akka.http.scaladsl.model.HttpResponse
import akka.util.Timeout
class RequestActor extends Actor {
//business logic - returns empty HttpResponse
def handleRequestMessage(requestContext : RequestContext) =
RouteResult.Complete(new HttpResponse())
override def receive = {
case reqContext : RequestContext =>
sender ! handleRequestMessage(reqContext)
}
}//end class RequestActor
object RouteActorTest extends App {
implicit val as = ActorSystem("RouteActorTest")
implicit val timeout = new Timeout(1000)
val actorRef = as actorOf Props[RequestActor]
def handleRequest(reqContext : RequestContext) : Future[RouteResult] =
ask(actorRef,reqContext).mapTo[RouteResult]
val route = path("") { get(handleRequest) }
//rest of application...
}//end object RouteActorTest
But this actually passes the response back every time to the previous Actor (the sender) until it reaches the main controller. Another thing about this option is that in the code, it says:
/**
* The result of handling a request.
*
* As a user you typically don't create RouteResult instances directly.
* Instead, use the methods on the [[RequestContext]] to achieve the desired effect.
*/
So I'm not sure if it's a recommended way of doing it.
I've tried using requestContext.complete() in another actor but it doesn't work (no error thrown, simply doesn't send response)
Does anybody know what the best way is to implement the previous architecture we had?
Thanks a lot!
the need to complete a request in another Actor without the need to
return it back to the controller
You don't need to complete the request in another actor. All you need to do is for the actor that handles the request to send back a result, and you can use complete to signal completion of the future:
case class MyRequest(data: Int)
case class MyResult(data: Int)
class RequestActor extends Actor {
override def receive: PartialFunction[Any, Unit] = {
case MyRequest(data) => sender ! MyResult(data + 1)
}
}
And route:
get {
path("yuval") {
import scala.concurrent.ExecutionContext.Implicits.global
implicit val timeout = Timeout(5 seconds)
complete {
val result = (completionActor ? MyRequest(1)).mapTo[MyResult]
result.map(r => HttpResponse(StatusCodes.OK, entity = s"Result was ${r.data}"))
}
}
}
If you want to handle to handcraft the HttpResponse yourself, you can always use context.complete:
get {
path("yuval") {
import scala.concurrent.ExecutionContext.Implicits.global
implicit val timeout = Timeout(5 seconds)
context => {
val result = (completionActor ? MyRequest(1)).mapTo[MyResult]
result.flatMap(r => context.complete(HttpResponse(StatusCodes.OK, entity = s"Result was ${r.data}")))
}
}
}
I want to test that an actor A send a message to an actor B after have received a message.
I'm using Play! 2.5 and I use the factories since I need to inject some of my classes and things like wSClient inside the actors.
The Actor A looks like:
object ActorA {
trait Factory {
def apply(ec: ExecutionContext, actorBRef: ActorRef): Actor
}
}
class ActorA #Inject()(implicit val ec: ExecutionContext,
#Named("actor-b") actorBRef: ActorRef)
extends Actor with ActorLogging with InjectedActorSupport {
override def receive: Receive = {
case i: Long =>
log info s"received $i"
actorBRef ! (i+1)
}
And the actor B is even more simple:
object ActorB {
trait Factory {
def apply(): Actor
}
}
class ActorB extends Actor with ActorLogging {
override def receive: Receive = {
case _ =>
log error "B received an unhandled message"
}
}
But my test doesn't pass, it is said that the expected message doesn't arrive, I get a Timeout in the test (but it is well logged by the actor B) so the problem comes from the test (and probably the Probe).
Here is the test:
val actorBProbe = TestProbe()
lazy val appBuilder = new GuiceApplicationBuilder().in(Mode.Test)
lazy val injector = appBuilder.injector()
lazy val factory = injector.instanceOf[ActorA.Factory]
lazy val ec = scala.concurrent.ExecutionContext.Implicits.global
lazy val factoryProps = Props(factory(ec, actorBProbe.ref))
val ActorARef = TestActorRef[ActorA](factoryProps)
"Actor B" must {
"received a message from actor A" in {
ActorARef ! 5L
actorBProbe.expectMsg(6L)
}
}
I also created a minimum Play! application with the code above available here.
In your test, actorBProbe is not the ActorB ref passed to ActorA constructor (of ref ActorARef). What really happens is that Guice creates a different ActorB (named actor-b), and passes its ref to ActorA (of ref ActorARef) constructor.
The test ends up with ActorB actor-b receiving 6L (as evident in log). While actorBProbe receives nothing.
The confusion really comes from mixing Guice lifecyle with Actors. In my experience, it creates more pains than I can bear.
To prove, simply print hash code of ActorRef's, you'll see they are different. Illustrated as followings:
val actorBProbe = TestProbe()
println("actorBProbe with ref hash: " + actorBProbe.ref.hashCode())
And,
class ActorA ... {
override def preStart =
log error "preStart actorBRef: " + actorBRef.hashCode()
// ...
}
In fact, even ec inside ActorA is not the same ec in the test code.
The following is a way to "force" the test to pass and at the same time prove that actorBProbe wasn't really being used by ActorB.
In stead of relying on Guice to "wire in" ActorB, we tell Guice to leave it alone by replacing #Named("actor-b") with #Assisted, like this,
import ...
import com.google.inject.assistedinject.Assisted
class ActorA #Inject()(...
/*#Named("actor-b")*/ #Assisted actorBRef: ActorRef)
...
Re-run the test, it'll pass. But this is probably not what you wanted to begin with.
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.
Below is my controller:
package controllers
import java.util.TimeZone
import akka.actor.{ActorNotFound, ActorSystem}
import akka.util.Timeout
import com.google.inject.Inject
import com.typesafe.akka.extension.quartz.QuartzSchedulerExtension
import play.api.Logger
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.libs.json.{JsValue, JsError, JsSuccess}
import play.api.mvc._
import scala.concurrent.Future
import scala.concurrent.duration._
class ScheduleController #Inject()(system: ActorSystem) extends Controller {
val scheduler = QuartzSchedulerExtension.get(system)
implicit val timeout = new Timeout(5.seconds)
def index = Action.async {
Future.successful(Ok("hi"))
}
def run = Action.async { request =>
// find actor and "start" it by sending message to it, return Ok() if found
// if actor is not found, return BadRequest()
// if error return InternalServerError()
}
}
I've been looking for tutorials but most are outdated since they deal with Play 2.3.
I solved a very similar problem with Play2 controllers and actors except in my case I'm not injecting an ActorSystem into my controller. Rather, I use Play's default actor system and create actors from my global settings object when my application starts. For a given actor I provide a selection method in its companion object. For example:
class MyActor extends Actor {
def receive = {
case Request => sender ! Response
}
}
object MyActor {
val path = // assuming fixed actor path
val sys = // reference to play actor system
def select: ActorSelection = sys.actorSelection(path)
// my actor messages
case object Request
case object Response
}
The way I have this actor setup I plan on asking it from inside my controller method using a Request and expect to process a Response object back from it. The controller is a non-actor sender in this scenario. By calling ask on the actor selection, I close over the future and map the result to a partial function that matches the expected response. Here's what my controller looks like:
class MyController extends Controller {
implicit val timeout = // set your timeout duration
def index = Action.async {
MyActor.select ? Request map {
case Response => Ok("success")
}
}
}
Testing this controller endpoint is VERY challenging because your actor might not be in a ready state to start handling messages. I spent about two days trying to figure out how to properly block on the my actor's lifecycle state using the actor selection I've exposed through its companion object. What I ended up doing inside my test class that worked for me was to create a method that takes in a function parameter and loops over my actor state until I can resolve its ActorRef. I'm a fan of ScalaTest so this is what my unit tests amounted to:
class MySpec extends FlatSpec with Matchers with BeforeAndAfterAll {
implicit val fakeApp: FakeApplication = new FakeApplication()
override def beforeAll() = Play.start(fakeApp)
override def afterAll() = Play.stop(fakeApp)
def awaitableActorTest(assertions: => Any): Unit = {
val timeout = 1.second
var isActorReady = false
while(!isActorReady) {
val futureRef = Await.ready(MyActor.select.resolveOne(timeout), timeout)
futureRef.value.get match {
case Success(_) =>
assertions
isActorReady = true
case Failure(_) =>
Thread.sleep(2000)
}
}
}
it should "process actor request and response via controller endpoint" in {
awaitableActorTest {
val result = route(routes.MyController.index).get
status(result) shouldBe OK
contentAsString(result) shouldBe "success"
}
}
}
What I get out of this awaitableActorTest pattern is a really clean way of reliably hitting my controller endpoint only when my actor is alive and available to process messages. If it's not, my ActorRef using its selection companion won't resolve in the future with success -- instead it completes with failure. When it fails I sleep a few seconds and repeat the loop. I break the loop when I its selection successfully resolved an ActorRef.
I'm unfamiliar with the QuartzSchedulerExtension to which you refer in your code snippet, but I hope my example is useful.
I have an akka actor:
class MyActor extends Actor {
def recieve { ... }
def getCount(id: String): Int = {
//do a lot of stuff
proccess(id)
//do more stuff and return
}
}
I am trying to create a unit test for the getCount method:
it should "count" in {
val system = ActorSystem("Test")
val myActor = system.actorOf(Props(classOf[MyActor]), "MyActor")
myActor.asInstanceOf[MyActor].getCount("1522021") should be >= (28000)
}
But it is not working:
java.lang.ClassCastException: akka.actor.RepointableActorRef cannot be cast to com.playax.MyActor
How could I test this method?
Do something like this:
import org.scalatest._
import akka.actor.ActorSystem
import akka.testkit.TestActorRef
import akka.testkit.TestKit
class YourTestClassTest extends TestKit(ActorSystem("Testsystem")) with FlatSpecLike with Matchers {
it should "count plays" in {
val actorRef = TestActorRef(new MyActor)
val actor = actorRef.underlyingActor
actor.getCount("1522021") should be >= (28000)
}
}
I generally recommend factoring any "business logic" that is executed by an Actor into a separate class that is supplied as a constructor parameter or provided via a Cake component. Doing this simplifies the Actor, leaving it only the responsibility to protect long-lived mutable state and handle incoming messages. It also facilitates testing both the business logic (by making it separately available for unit tests) and how the Actor interacts with that logic by supplying a mock / spy instance or component when testing the Actor itself.