Logging all messages sent to an Akka TestKit TestProbe - scala

I'm trying to log all messages received by a TestKit TestProbe, which is proving to be somewhat difficult. I'm aware of the Actor Logging section in the docs where it says one should use the debug.receive option in combination with a LogginReceive block. This however doesn't work when I'm not in control of the actor's implementation.
The only idea I had was subclassing akka.testkit.TestActor to use a LoggingReceive and then subclass TestKit to make it create instances of my TestActor subclass instead, but that didn't work because most functionality there is private to the akka namespace (and for good reason, I suppose).

There is a (probably surprisingly) simple answer:
probe.setAutoPilot(new TestActor.AutoPilot {
def run(sender: ActorRef, msg: Any) = {
log.debug("whatever")
this
}
})

Using Ronald's answer I wrote this to have a simpler way to define my probes:
object LoggingTestProbe {
def apply()(implicit system: ActorSystem) : TestProbe = {
val probe = TestProbe()
probe.setAutoPilot(new TestActor.AutoPilot {
def run(sender: ActorRef, msg: Any) = {
val other = sender.path
val me = probe.ref.path
system.log.debug(s"$me received $msg from $other")
this
}
})
probe
}
}
Having this I define my probes using LoggingTestProbe() instead of TestProbe().
I'm new to Scala so this might not be optimal but works great for me.

Sorry, got your question a little wrong at first, so here is my approach.
Create a wrapper actor, that logs the messages:
class LoggingActor(fac: => Actor) extends Actor {
val underlying = context.system.actorOf(Props(fac))
def receive = {
LoggingReceive {
case x ⇒ underlying.tell(x, sender)
}
}
}
and then just create your TestActorRef with your actor wrapped in the LoggingActor:
val echo = TestActorRef(new LoggingActor(new FooActor))
echo ! "hello world"

Related

Creating a scheduler/timer inside of akka typed actor

I'm trying to create a scheduler in my akka typed just to test it out, like to run every x seconds.
def start(): Behavior[ClientMsg] = Behaviors.setup { ctx =>
ctx.log.info("start() called")
ctx.system.scheduler.scheduleAtFixedRate(30.seconds, 5000.millis) { () =>
ctx.self ! TestMessage(""""this is pretty cool"""")
}
}
I am getting an error saying an implicit execution context is not in scope.
Where should I get the execution context from when inside of an typed actor?
Also, is this how I should be setting up a scheduler/timer?
Note that using Behaviors.withTimers { timers => ... } should be preferred over directly using the system scheduler as it handles removing scheduled sends if the actors stops etc.
In non-typed Akka the default ExecutionContext is the dispatcher object in the system object:
implicit val executionContext: ExecutionContext = ctx.system.dispatcher
I put this in a base class that I use for all Actors, along with default implicits for Timeout and ActorMaterializer.
You can use just Behaviors.withTimers or both Behaviors.setup and Behaviors.withTimers:
https://doc.akka.io/docs/akka/2.7.0/typed/actor-lifecycle.html
object HelloWorldMain {
final case class SayHello(name: String)
def apply(): Behavior[SayHello] =
Behaviors.setup { context =>
val greeter = context.spawn(HelloWorld(), "greeter")
Behaviors.receiveMessage { message =>
val replyTo = context.spawn(HelloWorldBot(max = 3), message.name)
greeter ! HelloWorld.Greet(message.name, replyTo)
Behaviors.same
}
}
}

How to get an actor reference (ActorRef) from ActorFlow?

According to the Play documentation on WebSockets the standard way to establish a WebSocket is to use ActorFlow.actorRef, which takes a function returning the Props of my actor. My goal is to get a reference to this underlying ActorRef, for instance in order to send a first message or to pass the ActorRef to another actor's constructor.
In terms of the minimal example from the documentation, I'm trying to achieve this:
class WebSocketController #Inject() (implicit system: ActorSystem, materializer: Materializer) {
def socket = WebSocket.accept[String, String] { request =>
val flow = ActorFlow.actorRef { out => MyWebSocketActor.props(out) }
// How to get the ActorRef that is created by MyWebSocketActor.props(out)?
// Fictitious syntax (does not work)
flow.underlyingActor ! "first message send"
flow
}
}
How can I get a reference to the actor that is created?
If it is not possible to get an ActorRef at this point (does it require materialization of the flow?), what would be the easiest way to store a reference to the created actor?
Using Actor.preStart() hook you can do some tricks to access the actorRef:
class MyWebSocketActor(
out: ActorRef,
firstMessage: Any,
actorRegistry: ActorRef
) extends Actor {
import play.api.libs.json.JsValue
override def preStart(): Unit = {
self ! firstMessage
actorRegistry ! self
}
...
}
def socket = WebSocket.accept[String, String] { request =>
ActorFlow.actorRef { out =>
Props(new MyWebSocketActor(out, "First Message", someRegistryActorRef))
}
}

Akka Actors unit testing for dummies

I'm newbie in Akka and Scala and i come from a non-concurrent world. Probably i'm doing a lot of things wrong, i will appreciate feedback even it's not related to the question.
I'm doing a simple chat application with Akka and Scala. I started (bc business requirements) by "typing feature"... it's the typical feature in whatsapp or tellegram "John is typing a message".
I have modelled it using two actors types: Talkers and Conversation, and I want to unit test my Conversation actor. My Conversation actor looks like this:
object Conversation {
def props(conversationId: UUID, talkers: List[ActorRef])(out: ActorRef) = Props(new Conversation(conversationId, talkers))
case class Typing(talkerId: TalkerId)
}
class Conversation(conversationId: UUID, talkers: List[ActorRef]) extends Actor with ActorLogging {
def receive = LoggingReceive {
case Typing(talkerId) =>
// notify all talkers that a talker is typing
// #TODO don't notify user which is typing
talkers foreach {talker: ActorRef => talker ! InterlocutorTyping(talkerId)}
}
}
I think, by now is very simple. So, before start coding in Scala and Akka I had tested this like:
I get my Conversation actor
I mock talkers
I send a message Typing to my actor
I expect that talkers should be notified
I don't really know if it's the correct approach in Scala and Akka. My test (using scalatest) looks like this:
"Conversation" should {
"Notify interlocutors when a talker is typing" in {
val talkerRef1 = system.actorOf(Props())
val talkerRef2 = system.actorOf(Props())
val talkerRef1Id = TalkerIdStub.random
val conversationId = UUID.randomUUID()
val conversationRef = system.actorOf(Props(classOf[Conversation], conversationId, List(talkerRef1, talkerRef2)))
// should I use TestActorRef ?
conversationRef ! InterlocutorTyping(talkerRef1Id)
// assert that talker2 is notified when talker1 is typing
}
}
Should I use TestActorRef? Should I use TestProbe() (I read that this is for integration tests)
How can I create Talker mocks? Is this approach correct?
It's correct to inject a List of Talkers to my conversation Actor?
I searched for documentation, but I think there are a lot too old and I'm not sure if the code examples are still functional.
Thank you for your time guys, and sorry about this noob question :=)
It's true that the testing situation in Akka is a little confusing to say the least.
In Akka generally you have two kinds of test, synchronous and asynchronous which some people term as 'unit' and 'integration' tests.
'Unit tests' are synchronous, you directly test the receive method without requiring an actor system, etc. In your case, you would want to mock the List[Talkers], call your receive method and verify that the send method is called. You can directly instantiate your actor with new Conversation(mockTalkers), it's not necessary in this case to use TestActorRef. For mocking, I recommend ScalaMock.
'Integration tests' are asynchronous, and generally test more than one actor working together. This is where you inherit TestKit, instantiate TestProbes to act as your talkers, use one to send a message to the Conversation actor, and verify that the other receives the InterlocutorTyping message.
It's up to you which kind of test you think is appropriate. My personal opinion is that unless you have complicated internal bevaviour in your actor, you should skip the synchronous tests and go straight for the asynchronous ('integration') tests as this will cover more tricky concurrency edge cases that you might otherwise miss. These are also more 'black-box' and so less sensitive to change as you evolve your design.
More details and code samples on the doc page.
Finally I did this (there are some more functionality than in the question):
object Conversation {
def props(conversationId: UUID)(out: ActorRef) = Props(new Conversation(conversationId))
case class TalkerTyping(talkerId: TalkerId)
case class TalkerStopTyping(talkerId: TalkerId)
case class Join(talker: ActorRef)
case class Leave(talker: ActorRef)
}
class Conversation(conversationId: UUID) extends Actor with ActorLogging {
var talkers : ListBuffer[ActorRef] = ListBuffer.empty
val senderFilter = { talker: ActorRef => talker != sender() }
def receive = LoggingReceive {
case Join =>
talkers += sender()
case Leave =>
talkers -= sender()
case TalkerTyping(talkerId) => // notify all talkers except sender that a talker is typing
talkers filter senderFilter foreach { talker: ActorRef => talker ! InterlocutorTyping(talkerId) }
case TalkerStopTyping(talkerId) => // notify all talkers except sender that a talker has stopped typing
talkers filter senderFilter foreach { talker: ActorRef => talker ! InterlocutorStopTyping(talkerId) }
}
}
And my test:
class ConversationSpec extends ChatUnitTestCase("ConversationSpec") {
trait ConversationTestHelper {
val talker = TestProbe()
val anotherTalker = TestProbe()
val conversationRef = TestActorRef[Conversation](Props(new Conversation(UUID.randomUUID())))
val conversationActor = conversationRef.underlyingActor
}
"Conversation" should {
"let user join it" in new ConversationTestHelper {
conversationActor.talkers should have size 0
conversationRef ! Join
conversationActor.talkers should have size 1
conversationActor.talkers should contain(testActor)
}
"let joining user leave it" in new ConversationTestHelper {
conversationActor.talkers should have size 0
conversationRef ! Join
conversationActor.talkers should have size 1
conversationActor.talkers should contain(testActor)
conversationRef ! Leave
conversationActor.talkers should have size 0
conversationActor.talkers should not contain testActor
}
"notify interlocutors when a talker is typing" in new ConversationTestHelper {
val talker1 = TestProbe()
val talker2 = TestProbe()
talker1.send(conversationRef, Join)
talker2.send(conversationRef, Join)
val talker2Id = TalkerIdStub.random
talker2.send(conversationRef, TalkerTyping(talker2Id))
talker1.expectMsgPF() {
case InterlocutorTyping(talkerIdWhoTyped) if talkerIdWhoTyped == talker2Id => true
}
talker2.expectNoMsg()
}
"notify interlocutors when a talker stop typing" in new ConversationTestHelper {
val talker1 = TestProbe()
val talker2 = TestProbe()
talker1.send(conversationRef, Join)
talker2.send(conversationRef, Join)
val talker2Id = TalkerIdStub.random
talker2.send(conversationRef, TalkerStopTyping(talker2Id))
talker1.expectMsgPF() {
case InterlocutorStopTyping(talkerIdWhoStopTyping) if talkerIdWhoStopTyping == talker2Id => true
}
talker2.expectNoMsg()
}
}
}

Should replies from child actor bubble up to its parent?

I'm new to Actors and I'm trying to understand how to chain actors properly. I've read Ask: Send-And-Receive-Future section of documentation.
Here is how I did it now
case class Message1(text : String)
class RootActor extends Actor {
def receive: Actor.Receive = {
case Message1(message) => {
(context.actorOf(Props( new TestActor1)) ? message)(5.seconds) pipeTo sender
}
}
}
class TestActor1 extends Actor {
def receive: Actor.Receive = {
case message : String => {
sender ! (message + " TestActor1")
}
}
}
object Test {
def main(args: Array[String]) {
println("Start!")
implicit val system = ActorSystem()
val rootActor = system.actorOf(Props( new RootActor))
for (reply1 <- (rootActor ? Message1("Begin"))(5.seconds).mapTo[String])
println(reply1)
}
}
So I manually relay reply. But shouldn't replies from child actor bubble up to its parent automatically ? Is this right way of doing it ?
Also I'm concerned that in manual way of bubbling up reply I use ask which requires timeout. But what if some child actor requires more time to do its job than others but I'm limited by one timeout value when I send message to root actor ? It seems to me that maintaining proper timeouts across entire chain is very cumbersome.
How can I simplify it ?
Just use forward instead. That's what it's there for. Use it like so :
context.actorOf(Props( new TestActor1)).forward(message)

Turning Scala Actors To Akka Actors: One Instance To Call Methods On

Recently I was switching from scala actors to akka actors, but noticed that akka actors use ActorRef instead of the instance object:
val actorRef: ActorRef = Actor.actorOf(new MyActor)
So I tried:
val myActor = new MyActor
val actorRef: ActorRef = Actor.actorOf(x)
... to have both: 1) ActorRef to send messages and 2) MyActor to call methods on.
But I got:
akka.actor.ActorInitializationException: ActorRef for instance of actor [MyActor] is not in scope.
So my question is: How can I obtain an instance (of some type) on which I can call ActorRef-like methods like ! AND also methods from the MyActor instance?
What you're doing is a terrible idea. So just stop right now, step away from the keyboard, and go to the Akka Documentation and read up on Actors.
Consider this:
class YourActor extends Actor {
var mutableStuff = ...
def receive = {
case _ =>
// mess with mutableStuff
}
def publicMethod = // mess with mutableStuff
}
Now, set up your system and start sending messages and calling that method from other threads. Boom!
You're doing precisely what Akka and the Actor model help you prevent. You're actually bending over backwards to break what they've already fixed :) They won't let you do it.
Now, you can unit test by accessing methods directly but you need a TestActorRef for that. While you're reading the docs, read through the section on Testing.
The best that I can up with is the following, quite dirty:
Is there a better way?
import akka.actor._
trait ActorCom {
var actorRefForInitialization: ActorRef = _
lazy val actorRef: ActorRef = actorRefForInitialization
def ?(message: Any)(implicit channel: UntypedChannel = NullChannel, timeout: Actor.Timeout = Actor.defaultTimeout) = actorRef ? message
def !(msg: Any)(implicit sender: UntypedChannel) = actorRef ! msg
def start = actorRef.start
}
object AkkaActorFactory {
def apply[A <: Actor](newInstance: => A with ActorCom): A with ActorCom = {
var instance: Option[A with ActorCom] = None
val actorRef = Actor.actorOf({
instance = Some(newInstance)
instance.get
})
instance.get.actorRefForInitialization = actorRef
instance.get.actorRef // touch lazy val in ActorCom, to make it equal to actorRef and then its fixed (immutable)
instance.get
}
}
class MyActor extends Actor {
def receive = {
case "test1" => println("good")
case "test2" => println("fine")
case _ => println("bad")
}
def sendTestMsg2Myself = self ! "test2"
}
val myActor = AkkaActorFactory(newInstance = new MyActor with ActorCom)
myActor.start
myActor ! "test1"
myActor.sendTestMsg2Myself // example for calling methods on MyActor-instance
myActor ! PoisonPill