Test Message Adapters in Akka Typed - scala

I am using Akka Typed (version 2.6.8) and I developed an actor that uses a message adapter.
object EncoderClient {
sealed trait Command
final case class KeepASecret(secret: String) extends Command
private final case class WrappedEncoderResponse(response: Encoded) extends Command
def apply(encoder: ActorRef[Encode]): Behavior[Command] =
Behaviors.setup { context =>
val encoderResponseMapper: ActorRef[Encoded] =
context.messageAdapter(response => WrappedEncoderResponse(response))
Behaviors.receiveMessage {
case KeepASecret(secret) =>
encoder ! Encode(secret, encoderResponseMapper)
Behaviors.same
case WrappedEncoderResponse(response) =>
context.log.info(s"I will keep a secret for you: ${response.payload}")
Behaviors.same
}
}
}
I want to test the effect of creation of the Message Adapter. I see that there is a class in the testkit library, MessageAdapter that seems to be a perfect fit to my needs.
However, I can't find anywhere an example of how to use it. Any help?

The MessageAdapter effect in the behavior testkit only signals that an adapter was created, as a result of a message or actor start. It won't help you with verifying that your adapter works.
You can inject a test probe for the encoder, expect a Encode message to get a hold of the message adapter ActorRef in the message. Something like this:
val probe = createTestProbe[Encode]()
val actor = spawn(EncoderClient(probe.ref))
actor ! KeepASecret("hush")
val encodeRequest = probe.receiveMessage()
encodeRequest ! Encoded(...)
// ... verify whatever happens on adapted response arriving...

I found a solution to my problem. If you're interested in testing if a message adapter is created by an actor for a particular type T, then you can use the BehaviorTestKit.expectEffectPF method.
It follows an example:
"EncoderClient" should "send an encoding request to the encode" in {
val encoder = TestInbox[Encode]()
val client = BehaviorTestKit(EncoderClient(encoder.ref))
client.run(KeepASecret("My secret"))
client.expectEffectPF {
case MessageAdapter(clazz, _) if clazz == classOf[Encoded] =>
}
}
If the object clazz is not of the tested type, the test fails.
Instead, if you're interested in testing that the message adapter works correctly, then, use the strategy suggested by johanandren.

Related

How to send message to actor via web socket in Play! framework?

This is rather a basic question but I couldn't find a satisfying answer after googling for hours. From the example in here, the way to make web socket is something like this:
Controller code:
import play.api.mvc._
import play.api.libs.streams.ActorFlow
import javax.inject.Inject
import akka.actor.ActorSystem
import akka.stream.Materializer
class Application #Inject()(cc:ControllerComponents) (implicit system: ActorSystem, mat: Materializer) extends AbstractController(cc) {
def socket = WebSocket.accept[String, String] { request =>
ActorFlow.actorRef { out =>
MyWebSocketActor.props(out)
}
}
}
Actor code:
import akka.actor._
object MyWebSocketActor {
def props(out: ActorRef) = Props(new MyWebSocketActor(out))
}
class MyWebSocketActor(out: ActorRef) extends Actor {
def receive = {
case msg: String =>
out ! ("I received your message: " + msg)
}
}
But how exactly do I send message from controller to the actor via web socket? Let's say in the controller code, I have an action code that handles when a button is pressed, it will send a block of string to the actor. How do I send this string to the actor above from the controller code?
I can provide you with some examples of websockets in Play. Essentially they use a Flow (akka-streams) to handle a websocket connection.
There is an official Play Websocket example: Lightbend's Websocket example
Based on that, I have several projects that use websockets, for example:
play-wsocket-scalajs
This is an example application showing how you can integrate a Play project with a Scala.js, Binding.scala project - using Web Sockets.
It is quite involved, so the easiest way is to check HomeController, UserParentActor, UserActor and AdapterActor how they work together.
scala-adapters
Is a framework that is based on the example above - that also shows how to register websocket clients.
Let's first understand what is already created, and what we need to add. The type of socket, is a WebSocket.
This WebSocket, reveals a single apply method:
def apply(request: RequestHeader): Future[Either[Result, Flow[Message, Message, _]]]
Therefore, as long as you did not send a message, the flow has not yet been created. Now, once a message is sent we can create the flow, and send it a meesage:
def index = Action.async(parse.json) { request =>
socket(request).map {
case Left(result) =>
Ok("Done: Left: " + result.body)
case Right(value) =>
Source.single(TextMessage(Json.stringify(request.body))).via(value).to(Sink.ignore).run()
Ok("Done: Right: ")
}
}
This sample app and related discussion might be helpful. Here's some code clipped/summarized from the linked sample app:
Flow.futureFlow(futureUserActor.map { userActor =>
val incomingMessages: Sink[Message, NotUsed] =
Flow[Message]
.map(...)
.to(...)
val outgoingMessages: Source[Message, NotUsed] =
ActorSource
.actorRef[User.OutgoingMessage](...)
.mapMaterializedValue { outActor =>
// give the user actor a way to send messages out
userActor ! User.Connected(outActor)
NotUsed
}
.map(...)
// then combine both to a flow
Flow.fromSinkAndSourceCoupled(incomingMessages, outgoingMessages)
})
There are at least two ways to approach this:
Customize play's ActorFlow.actorRef method to return the underlying actor. There was a similar discussion before, here's a gist. If you put the underlying actor into a (user, websocket) map, make sure to use a thread-safe implementation like the TrieMap.
What you're trying to do could be solved by creating an event bus & subscribing to it from within the actor. Then you could filter the events you're interested in & react accordingly. This solution is better, in that it actually scales - you can have more than one replica of your web app (the 1st approach wouldn't work in that case, because a replica that doesn't hold the reference to a user's WS actor could receive button clicked event). In pseudo code to illustrate the idea:
sealed trait AppEvent
final case class ButtonClicked(user: User.ID) extends AppEvent
// inside an action
system.eventStream.publish(ButtonClicked(request.identity.id))
// inside your actor
override def preStart =
context.system.eventStream.subscribe(self, classOf[AppEvent])
Please note that the idea of event bus is abstract. What I've demonstrated above is the most basic approach using akka's classic event bus, which works locally. For this approach to scale, you would need an actual message queue behind the scenes.

How to properly test actor behavior in case of failure

It is very important to know how a system might behave in case a particular actor fails - how does the recovery run, are the snapshots fetched properly etc. Surprisingly, I have not found any advice on it in the docs
An approach I have seen in some examples is to have a special Fail msg and let the actor throw an Exception on this msg.
class MyActor extends Actor {
override def receive =
...
case Fail => throw new Exception("kaboom")
}
I hope never to use it as it mixes production code and test code and would probably generate some reasonable WTFs in unsuspecting readers.
How would I induce an arbitrary actor to fail properly?
Thank you
After some thought, I have implemented a quick solution. The test package contains a trait for failing which is then mixed into actors for failure testing:
object Failing{
case object Fail
}
trait Failing {
def failOnMsg: PartialFunction[Any, Unit] = {
case Fail => throw new Exception("kaboom!")
}
}
then in the test the PF is prepended to the original receive
val props = Props(new MyActor(arg) with Failing {
override def receive = failOnMsg orElse super.receive
})
val actor = system.actorOf(props)
actor ! Fail
It is still better than what I had before, but brings some boilerplate. I hope there is a better solution.

Scala and Akka - Testing actors as a system with Akka Testkit

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.

Failing a scalatest when akka actor throws exception outside of the test thread

I've had a situation come up and bite me a few times where I'm testing an Actor and the Actor throws an exception unexpectedly (due to a bug), but the test still passes. Now most of the time the exception in the Actor means that whatever the test is verifying won't come out properly so it the test fails, but in rare cases that's not true. The exception occurs in a different thread than the test runner so the test runner knows nothing about it.
One example is when I'm using a mock to verify some dependency gets called, and due to a mistake in the Actor code I call an unexpected method in the mock. That causes the mock to throw an exception which blows up the actor but not the test. Sometimes this can even cause downstream tests to fail mysteriously because of how the Actor blew up. For example:
// using scala 2.10, akka 2.1.1, scalatest 1.9.1, easymock 3.1
// (FunSpec and TestKit)
class SomeAPI {
def foo(x: String) = println(x)
def bar(y: String) = println(y)
}
class SomeActor(someApi: SomeAPI) extends Actor {
def receive = {
case x:String =>
someApi.foo(x)
someApi.bar(x)
}
}
describe("problem example") {
it("calls foo only when it receives a message") {
val mockAPI = mock[SomeAPI]
val ref = TestActorRef(new SomeActor(mockAPI))
expecting {
mockAPI.foo("Hi").once()
}
whenExecuting(mockAPI) {
ref.tell("Hi", testActor)
}
}
it("ok actor") {
val ref = TestActorRef(new Actor {
def receive = {
case "Hi" => sender ! "Hello"
}
})
ref.tell("Hi", testActor)
expectMsg("Hello")
}
}
"problemExample" passes, but then downstream "ok actor" fails for some reason I don't really understand... with this exception:
cannot reserve actor name '$$b': already terminated
java.lang.IllegalStateException: cannot reserve actor name '$$b': already terminated
at akka.actor.dungeon.ChildrenContainer$TerminatedChildrenContainer$.reserve(ChildrenContainer.scala:86)
at akka.actor.dungeon.Children$class.reserveChild(Children.scala:78)
at akka.actor.ActorCell.reserveChild(ActorCell.scala:306)
at akka.testkit.TestActorRef.<init>(TestActorRef.scala:29)
So, I can see ways of catching this sort of thing by examining the logger output in afterEach handlers. Definitely doable, although a little complicated in cases where I actually expect an exception and that's what I'm trying to test. But is there any more direct way of handling this and making the test fail?
Addendum: I have looked at the TestEventListener and suspect there's maybe something there that would help, but I can't see it. The only documentation I could find was about using it to check for expected exceptions, not unexpected ones.
Thinking in Actors there is also another solution: failures travel to the supervisor, so that is the perfect place to catch them and feed them into the test procedure:
val failures = TestProbe()
val props = ... // description for the actor under test
val failureParent = system.actorOf(Props(new Actor {
val child = context.actorOf(props, "child")
override val supervisorStrategy = OneForOneStrategy() {
case f => failures.ref ! f; Stop // or whichever directive is appropriate
}
def receive = {
case msg => child forward msg
}
}))
You can send to the actor under test by sending to failureParent and all failures—expected or not—go to the failures probe for inspection.
Other than examining the logs, I can think of two ways to fail tests when an actor crashes:
Ensure that no Terminated message is received
Check the TestActorRef.isTerminated property
The latter option is deprecated, so I'll ignore it.
Watching Other Actors from Probes describes how to setup a TestProbe. In this case it might look something like:
val probe = TestProbe()
probe watch ref
// Actual test goes here ...
probe.expectNoMessage()
If the actor dies due to an exception it will generate the Terminated message. If that happens during the test and you expect something else, the test will fail. If it happens after your last message expectation, then the expectNoMessage() should fail when Terminated is received.
Okay, I've had a little time to play with this. I've got a nice solution that uses an event listener and filter to catch errors. (Checking isTerminated or using TestProbes is probably good in more focused cases but seems awkward when trying to make something to mix into any old test.)
import akka.actor.{Props, Actor, ActorSystem}
import akka.event.Logging.Error
import akka.testkit._
import com.typesafe.config.Config
import org.scalatest._
import org.scalatest.matchers.ShouldMatchers
import org.scalatest.mock.EasyMockSugar
import scala.collection.mutable
trait AkkaErrorChecking extends ShouldMatchers {
val system:ActorSystem
val errors:mutable.MutableList[Error] = new mutable.MutableList[Error]
val errorCaptureFilter = EventFilter.custom {
case e: Error =>
errors += e
false // don't actually filter out this event - it's nice to see the full output in console.
}
lazy val testListener = system.actorOf(Props(new akka.testkit.TestEventListener {
addFilter(errorCaptureFilter)
}))
def withErrorChecking[T](block: => T) = {
try {
system.eventStream.subscribe(testListener, classOf[Error])
filterEvents(errorCaptureFilter)(block)(system)
withClue(errors.mkString("Akka error(s):\n", "\n", ""))(errors should be('empty))
} finally {
system.eventStream.unsubscribe(testListener)
errors.clear()
}
}
}
You can just use withErrorChecking inline at specific spots, or mix it into a Suite and use withFixture to do it globally across all tests, like this:
trait AkkaErrorCheckingSuite extends AkkaErrorChecking with FunSpec {
override protected def withFixture(test: NoArgTest) {
withErrorChecking(test())
}
}
If you use this in my original example, then you will get the first test "calls foo only when it receives a message" to fail, which is nice because that's where the real failure is. But the downstream test will still fail as well due to the system blowing up. To fix that, I went a step further and used a fixture.Suite to instance a separate TestKit for each test. That solves lots of other potential test isolation issues when you have noisy actors. It requires a little more ceremony declaring each test but I think it's well worth it. Using this trait with my original example I get the first test failing and the second one passing which is just what I want!
trait IsolatedTestKit extends ShouldMatchers { this: fixture.Suite =>
type FixtureParam = TestKit
// override this if you want to pass a Config to the actor system instead of using default reference configuration
val actorSystemConfig: Option[Config] = None
private val systemNameRegex = "[^a-zA-Z0-9]".r
override protected def withFixture(test: OneArgTest) {
val fixtureSystem = actorSystemConfig.map(config => ActorSystem(systemNameRegex.replaceAllIn(test.name, "-"), config))
.getOrElse (ActorSystem (systemNameRegex.replaceAllIn(test.name, "-")))
try {
val errorCheck = new AkkaErrorChecking {
val system = fixtureSystem
}
errorCheck.withErrorChecking {
test(new TestKit(fixtureSystem))
}
}
finally {
fixtureSystem.shutdown()
}
}
}

Akka - Common service actor: Identify or Extension

Lets say I have some commonly used by other actors service-layer actor. For example, an registry service that stores and retrieves domain objects:
case class DomainObject(id: UUID)
class Registry extends akka.actor.Actor {
def receive: Receive = {
case o: DomainObject => store(o) // save or update object
case id: UUID => sender ! retrieve(id) // retrieve object and send it back
}
}
I do not want to explicitly pass instance of such registry into all actors who may use it. Instead of it, I want them to be able to somehow 'locate' it.
For this I can think of two solutions:
Identify message: each registry user actor knows registry actor name from some configuration and able to sent identification message to it. After AgentIdentity message is received back we are good to go:
val registryName = ... // some name
val registryId = ... // some id
var registry = _
def preStart() {
context.actorSelection(registryName) ! Identify(registryId)
}
def receive: Receive = {
case ActorIdentity(`registryId`, ref) => registry = ref
}
I do not like this way because right after user actor initialisation there is a phase when we do not know if there is a registry in system et all and thus do not know will we ever be able to operate or not.
Akka Extensions: I can create an extension which would:
a. create instance of Registry actor in given Actor System on initialization;
b. return this actor to user who needs it via some method in Extension.
object RegistryKey extends ExtensionKey[RegistryExtension]
class RegistryExtesion(system: ExtendedActorSystem) extends RegistryKey {
val registry = system.actorOf(Props[Registry], "registry")
}
The question is: which method is better and are Akka Extesions can be used for this at all?
I think the extension idea is a good one as long as your registry actor is always going to be in the same ActorSystem.
Alternatively, using actorSelection (adapted from Remote Lookup):
class RegistryClient extends Actor {
val path = "/path/to/registry/actor"
context.setReceiveTimeout(3.seconds)
def sendIdentifyRequest(): Unit =
context.actorSelection(path) ! Identify(path)
def receive = {
case ActorIdentity(`path`, Some(ref)) ⇒
context.setReceiveTimeout(Duration.Undefined)
context.become(active(ref))
case ActorIdentity(`path`, None) ⇒
throw new RuntimeException("Registry not found")
case ReceiveTimeout ⇒ sendIdentifyRequest()
}
def active(registry: ActorRef): Actor.Receive = {
// use the registry
}
}
This will work for remote or local actors.
Let's look at the extension solution. Actors are created asynchronously. Therefore your extension constructor won't fail when calling actorOf if the actor fails to initialize.
If you want to know for sure that the actor failed to initialize then one way to know is to ask the actor something that it will respond to and Await a response. The Await will throw a TimeoutException if the actor fails to respond.
class RegistryExtension(system: ExtendedActorSystem) extends Extension {
val registry = system.actorOf(Props[Registry], "registry")
implicit val timeout: Timeout = Timeout(500.millis)
val f = registry ? "ping" // Registry should case "ping" => "pong"
Await.result(f, 500.millis) // Will throw a TimeoutException if registry fails
// to respond
}
The TimeoutException will get thrown when you call RegistryExtension(system).registry the first time.
How about the Cake Pattern or a Dependency Injection library such as subcut.
Derek Wyatt mentions DI in his book 'Akka Concurrency' instead of using too much actorFor to look up actors:
http://www.artima.com/forums/flat.jsp?forum=289&thread=347118