How to mock an Akka Actor to Unit Test a class? - scala

I have a Controller class which controls the requests sent to a Akka actor which is injected in to the controller.
Controller's code:
class Controller(actor: ActorRef) {
def control(msg: String): Future[String] = {
actor.ask(msg)(Timeout(2 seconds)).mapTo[String]
}
}
And my Actor's code is:
class ActorA extends Actor {
override def receive: Receive = {
case msg: String => sender ! msg
case msg: Int => sender ! msg.toString
case _ => "Invalid command!"
}
Now I need to mock ActorA's behaviour to unit test Controller. Is there a way to do so via Akka TestKit ?

Use a TestProbe. From the testing documentation:
val probe = TestProbe()
val future = probe.ref ? "hello"
probe.expectMsg(0 millis, "hello")
probe.reply("world")
assert(future.isCompleted && future.value == Some(Success("world")))

Related

unable to create another actor in Akka WebSocket

class HomeController #Inject (implicit actorSystem:ActorSystem, materializer :Materializer) extends Controller
{
case class sendMsg(msg:JsValue)
class MYWSACTOR(out:ActorRef,mainActor:ActorRef) extends Actor
{
def receive =
{
case msg : JsValue =>
mainActor ! sendMsg(msg)
case sendMsg(msg) =>
{
/* validation part */
out ! msg
}
}
}
}
val mainActor = actorSystem.actorOf(Props[MYWSACTOR],"mainActor")
def object MYWSACTOR
{
def props(out:ActorRef) = Props(new MYWSACTOR(out,mainActor))
}
def socket = WebSocket.accept[JsValue,JsValue]
{
request =>
ActorFlow.actorRef(out => MYWSACTOR.props(out))
}
I am new to Akka and Scala . I am trying to make a chat application using Akka in Scala and Play framework 2.5.3 .I used the above code from the official documentation. I just wanted to create another actor (mainActor in above code) so that message from the client is validated first and then be sent back. But the problem is mainActor is unable to send the msg to another class case sendMsg. Also , if i try to create the actor at any different point, it gives me compilation error Unexpected Exception: Provisional Exception
Here is hopefully what you probably wanted:
case class SendMsg(msg: JsValue)
case class ReceivedMessage(wsActor: ActorRef, msg: JsValue)
class MyWsActor(out: ActorRef, mainActor:ActorRef) extends Actor {
def receive = {
case msg : JsValue =>
mainActor ! ReceivedMessage(self, msg)
case SendMsg(msg) =>
out ! msg
}
}
object MyWsActor {
def props(out: ActorRef, mainActor: ActorRef) = Props(new MyWsActor(out, mainActor))
//or if you want to instantiate your Props with reflection
def props2(out: ActorRef, mainActor: ActorRef) = Props(classOf[MyWsActor], out, mainActor)
}
class MainActor() extends Actor {
def receive = {
case ReceivedMessage(wsActor, msg) => {
/*
Do sth with msg
*/
val result = msg
wsActor ! SendMsg(result) //here sender is your MyWsActor
}
}
}
class HomeController #Inject() (
actorSystem: ActorSystem,
materializer: Materializer
) extends Controller {
val mainActor = actorSystem.actorOf(Props[MainActor], "mainActor")
def socket = WebSocket.accept[JsValue,JsValue] {
request =>
ActorFlow.actorRef(out => MyWsActor.props(out, mainActor))
}
}
The key is that you can send, with actual message, also ActorRef (or by default you can get a sender ActorRef calling sender()) and then you can replay to this ActorRef.

Expect message on mocked actor when testing with TestKit

I am trying to test an actor, that depends on another actor. In the following test ActorA sends MessageA to ActorB and expects a MessageB in return.
Since this is a Unit test for ActorB, I am mocking ActorA. My problem now is, that i want to be sure that ActorA gets MessageA, but when i call the expectMsg (see the commented line) the whole autopilot does not seem to work anymore.
Am I doing something wrong here?
class MyTest(_system: ActorSystem) extends TestKit(_system)
with WordSpecLike
with BeforeAndAfterAll {
object Start
object MessageA
object MessageB
def this() = this(ActorSystem("TestSpec"))
override def afterAll() {
system.shutdown()
}
// will later be mocked
class ActorA extends Actor {
def receive = {
case MessageA => sender() ! MessageB
}
}
class ActorB(actorA: ActorRef) extends Actor {
def receive = {
case Start => actorA ! MessageA
case MessageB => println("Got MessageB")
}
}
"" should {
"work" in {
val actorA = TestProbe()
actorA.setAutoPilot(new AutoPilot {
override def run(sender: ActorRef, msg: Any): AutoPilot = {
msg match {
case MessageA => sender ! MessageB
}
TestActor.KeepRunning
}
})
// this will break the test
//actorA.expectMsg(MessageA)
val actor = system.actorOf(Props(new ActorB(actorA.ref)))
actor ! Start
}
}
}
You should expect MessageA after sending start to your ActorB.
val actor = system.actorOf(Props(new ActorB(actorA.ref)))
actor ! Start
// this will not break the test
actorA.expectMsg(MessageA)

How could actor know if it has all the responses it needs in order to proceed?

I am fairly new to akka and not sure about approaching this problem.
I have a Monitor actor that spawns 2 other actors as DiskMonitor and MemoryMonitor
DiskMonitor checks the diskUsage and report DiskReport message
MemoryMonitor checks the diskUsage and report MemoryReport message
I want to combine the DiskReport and MemoryReport into Report Object and send to to remote machine via an API.
Questions
Since DiskMonitor and MemoryMonitor both are actors they send the response back to caller once they are done with their work
Monitor
diskMonitor ! DiskMonitorRequest
memoryMonitor ! MemoryMonitorRequest
How would Monitor know that it has all the results that it needs so that it can create Report object and send it via API?
Are actors good way to approach this problem?
I also read about future but I don't understand them well and not sure if they would be helpful in this problem context
This is a simple way of doing these things. Other options can be through using context.become or the trait FSM.
class Monitor extends Actor {
// Somewhere do this:
diskMonitor ! DiskMonitorRequest
memoryMonitor ! MemoryMonitorRequest
var diskMonitorResult = Option.empty[DiskMonitor]
var memoryMonitorResult = Option.empty[MemoryMonitor]
def recieve = {
case d: DiskMonitor =>
diskMonitorResult = Some(d)
checkIfCompleted()
case m: MemoryMonitor =>
memoryMonitorResult = Some(m)
checkIfCompleted()
}
def checkIfCompleted = {
(diskMonitorResult, memoryMonitorResult) match {
case (Some(diskMonitor), Some(memoryMonitor)) =>
// send to external API
externalApi ! Report(diskMonitor, memoryMonitor)
// Possibly stop this actor
case _ => // do nothing
}
}
}
You can use ask pattern and combine futures..
import akka.actor._
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.ExecutionContextExecutor
import scala.concurrent.duration._
import scala.language.postfixOps
class Monitor extends Actor with ActorLogging {
implicit val timeout: Timeout = Timeout(5 seconds)
implicit val ec: ExecutionContextExecutor = context.dispatcher
val diskM: ActorRef = context.system.actorOf(Props[DiskMonitor])
val memoryM: ActorRef = context.system.actorOf(Props[MemoryMonitor])
val remoteM: ActorRef = context.system.actorOf(Props[RemoteMachine])
override def preStart: Unit = {
log.info("Monitor Created..")
self ! GenerateReport
}
override def receive: Receive = {
case GenerateReport =>
(diskM ? MakeDiskReport).mapTo[DiskReport].zip((memoryM ? MakeMemoryReport)
.foreach { case (diskR, memR) =>
remoteM ! Report(memR.report, diskR.report)
}
}
}
class DiskMonitor extends Actor with ActorLogging {
override def receive: Receive = {
case MakeDiskReport =>
log.info("Creating DiskReport..")
val client = sender
client ! DiskReport("DiskReport")
}
}
class MemoryMonitor extends Actor with ActorLogging {
override def receive: Receive = {
case MakeMemoryReport =>
log.info("Creating MemoryReport..")
val client = sender
client ! MemoryReport("MemoryReport")
}
}
class RemoteMachine extends Actor with ActorLogging {
override def receive: Receive = {
case Report(memr, diskr) =>
log.info(s"Final Report.. $memr, $diskr")
}
}
object Main extends App {
val sys = ActorSystem()
sys.actorOf(Props[Monitor])
}
sealed trait Message
case object GenerateReport extends Message
case object MakeDiskReport extends Message
case object MakeMemoryReport extends Message
case class DiskReport(report: String) extends Message
case class MemoryReport(report: String) extends Message
case class Report(memReport: String, diskReport: String) extends Message

Private Communication between client and server using websockets play 2.3.x

I am newbie to scala and can't figure out sending private message to client using websocket.
Here is my controller:
object Client extends Controller {
def socket(uuid: String) = WebSocket.acceptWithActor[String, String] { request =>
out => ClientWebSocket.props(uuid)
}
// Test Method to send message to websocket connected client
def sendMessage(guid: String) = Action { implicit request =>
val system = ActorSystem("default")
val out = system.actorOf(Props(classOf[ClientWebSocket], guid))
out ! SendUpdate("Message Recieved")
Ok
}
}
Here is my actor class:
object ClientWebSocket {
def props(uuid: String) = Props(new ClientWebSocket(uuid))
case class SendUpdate(msg:String)
}
class ClientWebSocket(uuid: String) extends Actor {
import ClientWebSocket._
def receive = {
case SendUpdate(msg:String) =>
sender ! "Message is " + msg
}
}
When I call sendMessage with uuid of client, I am getting akka dead letters encountered error.
Any help is really appreciated.
First off, in your WebSocket method, you need to use the provided ActorRef instead of sender. sender is probably something else in the hierarchy.
class ClientWebSocket(uuid: String, out: ActorRef) extends Actor {
import ClientWebSocket._
def receive = {
case SendUpdate(msg: String) =>
out ! s"Message is $msg"
}
}
Second, you are receiving dead letters in sendMessage because you are replying to your controller, which unfortunately is not an Actor.
The problem is that you cannot get hold of the ActorRef and since you don't know the name of the actor, you cannot use an ActorSelection. So you would need to make a WebSocket call to your own app, call it from web browser / JavaScript or do some hacking with the action to get to the actor.
Edit
Your ClientWebSocket could register (e.g. in preStart via ActorSelection or via an ActorRef as props parameter) with another actor that keeps a Map[String, ActorRef] reference to all websockets and in turn monitors them using death watch. That actor would then forward your SendUpdate to the correct websocket actor. Since you can only return Props in acceptWithActor, you are not able to form a real hierarchy.
The out parameter is an ActorRef, you will use it to send messages to the Client, therefore you need to keep it in your Actor.
object Client extends Controller {
def socket(uuid: String) = WebSocket.acceptWithActor[String, String] { request =>
// out is an actor to what the outbound messages should go to respond to remote client
out => ClientWebSocket.props(uuid, out)
}
}
Your ClientWebSocket actor is as follows:
class ClientWebSocket(uuid: String, out: ActorRef) extends Actor ...
And the companion object is as follows
object ClientWebSocket {
def props(uuid: String, out: ActorRef) = Props(classOf[ClientWebSocket], uuid, out)
}
In the ClientWenSocket you can Register/Deregister the Actor with a CentralWebSocketControl Actor as:
class ClientWebSocket(uuid: String, out: ActorRef) extends Actor {
#throws[Exception](classOf[Exception])
override def preStart(): Unit = {
super.preStart()
CentralWebSocketControl.instance ! RegisterMe(uuid)
}
#throws[Exception](classOf[Exception])
override def postStop(): Unit = {
CentralWebSocketControl.instance ! UnregisterMe(uuid)
super.postStop()
}
}
The CentralWebSocketControl Actor can be as:
class CentralWebSocketControl extends Actor {
val clientActors = scala.collection.mutable.HashMap[String, ActorRef]()
override def receive: Actor.Receive = {
case RegisterMe(uuid) =>
clientActors += uuid -> sender()
case UnregisterMe(uuid) =>
clientActors -= uuid
}
}
object CentralWebSocketControl {
val instance = Akka.system.actorOf(Props(classOf[CentralWebSocketControl]), name = "CentralWebSocketControl")
}
To send a message to a given ClientWebSocket identified by an uuid, you can send a message to CentralWebSocketControl that may delegate the message to the registered ClientWebSocket.
class CentralWebSocketControl extends Actor {
val clientActors = scala.collection.mutable.HashMap[String, ActorRef]()
override def receive: Actor.Receive = {
...
case SendUpdateTo(uuid: String, msg: String) =>
clientActors.get(uuid) match {
case Some(actor) =>
actor ! SendUpdate(msg)
case None =>
???
}
}
}
and finally
class ClientWebSocket(uuid: String, out: ActorRef) extends Actor {
override def receive: Receive = {
case SendUpdate(msg) =>
out ! msg
}
...
}

Akka intercepting receive with stackable behavior

Akka and Scala newbie here, please feel free to edit the question as necessary in order to clearly articulate my intent in the domain of Scala and Akka.
Before I show code snippets, here's the problem I want to solve: I essentially want to develop a common module for my team to use when they're developing their applications using Akka actors. I want to allow them to mixin a trait which will extend their receive functionality at runtime, mainly for logging purposes. I'm running into compile errors, which I'll explain soon.
But first, take for example, a simple main:
object Test extends App {
val system = ActorSystem("system")
val myActor = system.actorOf(Props(new MyActor), "myActor")
myActor ! "Hello world!"
}
Here's an example implementation of an actor that a team member might implement in his application:
class MyActor extends Actor with ActorLogger {
override def receive: Receive = {
case msg => {
log.info("testing ...")
}
case _ => throw new RuntimeException("Runtime Ex")
}
}
And here's an example of how I would provide a common trait for them to mixin:
trait ActorLogger extends Actor {
val log: DiagnosticLoggingAdapter = Logging(this)
abstract override def receive: Receive = {
case msg: Any => {
if (msg.isInstanceOf[String]) {
println("enter")
log.mdc(Map[String, Any]("someKey" -> 123))
super.receive(msg)
log.clearMDC()
println("exit")
}
}
case _ => throw new RuntimeException("Runtime Ex")
}
}
As you can see, I'm trying to add data to an MDC if the message so happens to be String (a basic example, in reality, I would check for some custom type of our own).
The error I get is:
Error:(29, 16) overriding method receive in trait ActorLogger of type =>
MyActor.this.Receive;
method receive needs `abstract override' modifiers
override def receive: Receive = {
^
What's wrong here? And is stackable traits the right to go away to achieve something like this? If not, what is the most idiomatic way?
More generally, is there another pattern being applied here besides "interceptor" pattern?
Thanks for all the help!
A solution without a hack with akka package:
import akka.actor.{Actor, ActorSystem, Props}
trait MyActorExtension extends Actor {
def receiveExtension: Receive = PartialFunction.empty
}
abstract class MyActor extends MyActorExtension {
protected def receiveMsg: Receive
def receive: Receive = receiveExtension orElse receiveMsg
}
trait ActorLogger1 extends MyActor with MyActorExtension {
abstract override def receiveExtension = {
case msg =>
println(s"********** Logging # 1: $msg")
super.receiveExtension.applyOrElse(msg, receiveMsg)
}
}
trait ActorLogger2 extends MyActor with MyActorExtension {
abstract override def receiveExtension = {
case msg =>
println(s"########## Logging # 2: $msg")
super.receiveExtension.applyOrElse(msg, receiveMsg)
}
}
class SpecificActor extends MyActor with ActorLogger1 with ActorLogger2 {
def receiveMsg = {
case someMsg =>
println(s"SpecificActor: $someMsg")
}
}
object Test extends App {
val system = ActorSystem("system")
val mySpecificActor = system.actorOf(Props(new SpecificActor), "SpecificActor")
mySpecificActor ! "Hello world!"
}
#### Logging # 2: Hello world!
****** Logging # 1: Hello world!
SpecificActor: Hello world!
aroundReceive is for Akka internal use and the stackable trair pattern is not that comfortable for this case.
I recommend you using Receive Pipeline for easy message interception.
I think that you need something like this
package akka
import akka.MsgsProt._
import akka.actor.{ Actor, ActorSystem, Props }
import scala.concurrent.duration._
sealed trait MsgProt
object MsgsProt {
case object FooMsg extends MsgProt
case object BarMsg extends MsgProt
}
trait Foo extends Actor {
override protected[akka] def aroundReceive(receive: Actor.Receive, msg: Any): Unit = msg match {
case FooMsg => println("Foo message")
case msg => super.aroundReceive(receive, msg)
}
}
trait Bar extends Actor {
override protected[akka] def aroundReceive(receive: Actor.Receive, msg: Any): Unit = msg match {
case BarMsg => println("Bar message")
case msg => super.aroundReceive(receive, msg)
}
}
class MyActor extends Actor with Foo with Bar {
override def receive: Actor.Receive = {
case _ => println("Nothing I know")
}
}
object Foo extends App {
val system = ActorSystem("foobar")
val myActor = system.actorOf(Props[MyActor])
implicit val timeout = 2 seconds
myActor ! FooMsg
myActor ! BarMsg
myActor ! "wrong message"
system.awaitTermination(10 seconds)
}
The output of this program is:
Foo message
Bar message
Nothing I know
Most important part is that package declaration - akka. Because method aroundReceive is limited only to akka package so you have to have some.package.akka and inside you can use that method aroundReceive. I think that it looks more like a hack not a solution but works. You can see more usage of this inside Akka itself ex. akka.DiagnosticActorLogging. But this is basically solution that you want to do with stacking Actors behaviour.