Skip to main content

Akka Concepts - Testing Actor Systems

Remember that actors interact only using message passing. In order to check actors behavior you can do it through the messages sent to them and back from them.  So how do you test actors? You send them messages :)
To test actors that communicate only with messages, you need to send it a message and get reply back and check it.
akka has a TestProbe
val p = TestProbe(); // record incoming messages in queue so you can assert and verify them.
Creating actor system for tests:
implicit val system = ActorSystem("TestSys")
val toggle = system.actorOf(Props[Toggle]) // this is the actor we are going to test.
val p = TestProbe() // this is the test client actor which will record the messages.

p.send(toggle, "How are you") // probe --> tested actor: how are you?
p.exepectMsg("happy") // assert result is happy.
To have the probe actor created for you:
new TestKit(ActorSystem("TestSys")) with ImplicitSender { // we are in probe actor.
  val toggle = system.actorOf(Props[Toggle])
  toggle ! "how are you?" // we are already in probe actor.
}
managing external dependencies
Some of your actors might need to access database what do you do it?
  1. Dependency Injection
  2. Just Override - Add overridable factory methods
class DBManager extends Actor {
  def props: Props = Props[DBWorker] // Override this method when you want a fakeDB!
  
  def receive = {
    ...
    // Do not use context.actorOf(Props[DBWorker]) instead use the factory method see below:
    val dbworker = context.actorOf(props, "dbworker") // So we can override the def!
    ...
  }
}
In our case we have used def props and we refer to it when creating the actor.
How to probe messages sent to parent?
The parent is the Guardian actor if the system created it for us.
So we need a StepParent which forwards all messages to probe.
class StepParent(child: Props, probe: ActorRef) extends Actor {
  contex.actorOf(child, "child")
  def receive = {
    case msg => probe.tell(msg, sender) // forward all messages to probe!
  }
}
In a similar way we can create FosterParent actor which listens to all messages in both directions from child to parent and vice versa.
Define your test class
class GetterSpec extends TestKit(ActorSystem("GetterSpec")) with ImplicitSender ...
A test case snippet:
system.actorOf(Props(new StepParent(fakeDBWorker, testActor), "name")) // testActor is given by framework it's the probe.
expectMsg(IDidMyWorkMsg) // we expect a message IDidMyWorkMsg
So in general you send messages to your actors and you use expectMsg to check the messages they send back.
BOOK: the best book I have found for working with akka actors, it's surprisingly much better than all others, as it drives you through of a flow of building an app, making mistakes and the continuously improving the design of your app.  The book is: "Mastering Akka"


Comments

Popular posts from this blog

Functional Programming in Scala for Working Class OOP Java Programmers - Part 1

Introduction Have you ever been to a scala conf and told yourself "I have no idea what this guy talks about?" did you look nervously around and see all people smiling saying "yeah that's obvious " only to get you even more nervous? . If so this post is for you, otherwise just skip it, you already know fp in scala ;) This post is optimistic, although I'm going to say functional programming in scala is not easy, our target is to understand it, so bare with me. Let's face the truth functional programmin in scala is difficult if is difficult if you are just another working class programmer coming mainly from java background. If you came from haskell background then hell it's easy. If you come from heavy math background then hell yes it's easy. But if you are a standard working class java backend engineer with previous OOP design background then hell yeah it's difficult. Scala and Design Patterns An interesting point of view on scala, is

Alternatives to Using UUIDs

  Alternatives to Using UUIDs UUIDs are valuable for several reasons: Global Uniqueness : UUIDs are designed to be globally unique across systems, ensuring that no two identifiers collide unintentionally. This property is crucial for distributed systems, databases, and scenarios where data needs to be uniquely identified regardless of location or time. Standardization : UUIDs adhere to well-defined formats (such as UUIDv4) and are widely supported by various programming languages and platforms. This consistency simplifies interoperability and data exchange. High Collision Resistance : The probability of generating duplicate UUIDs is extremely low due to the combination of timestamp, random bits, and other factors. This collision resistance is essential for avoiding data corruption. However, there are situations where UUIDs may not be the optimal choice: Length and Readability : UUIDs are lengthy (typically 36 characters in their canonical form) and may not be human-readable. In URLs,

Bellman Ford Graph Algorithm

The Shortest path algorithms so you go to google maps and you want to find the shortest path from one city to another.  Two algorithms can help you, they both calculate the shortest distance from a source node into all other nodes, one node can handle negative weights with cycles and another cannot, Dijkstra cannot and bellman ford can. One is Dijkstra if you run the Dijkstra algorithm on this map its input would be a single source node and its output would be the path to all other vertices.  However, there is a caveat if Elon mask comes and with some magic creates a black hole loop which makes one of the edges negative weight then the Dijkstra algorithm would fail to give you the answer. This is where bellman Ford algorithm comes into place, it's like the Dijkstra algorithm only it knows to handle well negative weight in edges. Dijkstra has an issue handling negative weights and cycles Bellman's ford algorithm target is to find the shortest path from a single node in a graph t