whoami

Lucas Jenß

cat /etc/motd

The Coding Journal ツ — Notes taken on an epic coding journey. Technical solutions, debugging notes, and practical guides from the trenches of software development.

Close-up of white dominoes with black dots standing on green felt surface, shallow depth of field, focused mood

ls -la ~/languages/

total 8
drwxr-xr-x
▶ PHP
▶ Ruby
▶ Scala
▶ C#
▶ JavaScript
▶ Objective-C
▶ Shell Scripting

ls -la ~/toolchain/

total 7
drwxr-xr-x
▶ Typo3
▶ Akka
▶ Capistrano
▶ Git
▶ MAMP
▶ Adobe Illustrator
▶ NSTrackingArea (Cocoa)

uname -a

platforms
drwxr-xr-x
▶ Mac OS X
▶ Unix

Akka testing patterns with TestProbe

An actor test should answer a business question: did the right message reach the right component with the right payload? TestProbe turns that question into explicit expectations, replacing timing sleeps with observable exchanges. It suits payment, booking and telemetry flows where several actors cooperate to complete one outcome.

Sydney and Melbourne are major centres for Australian fintech, commerce and logistics teams. A weekday journey can involve a Sydney train or Melbourne tram where mobile coverage briefly drops, making reconnection and timeout behaviour ordinary test scenarios. An actor suite that reproduces those conditions without a live network is useful well beyond a developer laptop.

The pattern is simple but easy to misuse. A probe does not inspect every actor automatically; it receives messages delivered to its own mailbox. Map the flow first, decide which edges form the contract, and let each assertion express one edge. Classic Akka’s TestKit and ScalaTest support this cleanly, although Typed actors use a related API with different syntax.

Set up a deterministic actor test

Start with the runtime you ship. For Classic Akka, use akka-actor and akka-testkit at one version, with ScalaTest or the project’s existing test framework. Typed and Classic ActorRef instances should not be mixed without a clear boundary.

libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-actor" % akkaVersion,
  "com.typesafe.akka" %% "akka-testkit" % akkaVersion % Test
)

Pin that version in one build property and keep test-only dependencies out of production packaging. Each specification needs an isolated ActorSystem so actor names, mailboxes and supervision settings cannot leak between tests.

For an Android companion client, keep native code out of JVM unit tests and exercise that boundary through device or emulator runs. This note on Android NDK libraries explains the CMake-based build side, allowing the actor contract to remain a fast JVM-only specification.

Model the message flow before asserting

Begin with a route map, not a list of assertions. Suppose a gateway sends Pay to a coordinator, the coordinator forwards it to a processor, and the processor reports ChildCompleted through its parent. The contract might include input to the coordinator, coordinator to processor, and processor to parent. An audit event sent elsewhere is a separate edge.

Keep those edges visible in types. Separate commands from events, attach stable correlation identifiers, and use immutable case classes. Names should describe intent, such as Pay, ProcessorSaw and PaymentAccepted, rather than expose generic string messages or internal method calls.

An Australian release detail offers useful timing coverage. A team freezing deployments around Australia Day on 26 January may pause a scheduler while allowing in-flight payments to finish. A FreezeRequested command should reach the scheduler, and its acknowledgement should return before another PublishScheduled event is accepted. A probe can verify that sequence without waiting for the date or touching a deployment system.

Assert delivery, ordering and state

Here is a small Classic Akka flow with a probe acting as both the processor’s event sink and the coordinator’s reply sink.

import akka.actor.{Actor, ActorRef, Props}
import akka.testkit.{TestActorRef, TestProbe}

sealed trait PaymentCommand
final case class Register(result: ActorRef) extends PaymentCommand
final case class Pay(id: String, cents: Long) extends PaymentCommand

sealed trait PaymentEvent
final case class ProcessorSaw(id: String, cents: Long) extends PaymentEvent
final case class PaymentAccepted(id: String) extends PaymentEvent
final case class ChildCompleted(id: String) extends PaymentEvent

final class Coordinator extends Actor {
  private var result: Option[ActorRef] = None
  private var processor: Option[ActorRef] = None

  def receive = {
    case Register(client) =>
      result = Some(client)
      processor = Some(
        context.actorOf(Props(new Processor(client)), "processor")
      )
    case command: Pay =>
      processor.foreach(_ ! command)
    case completed: ChildCompleted =>
      result.foreach(_ ! PaymentAccepted(completed.id))
  }
}

final class Processor(client: ActorRef) extends Actor {
  def receive = {
    case command: Pay =>
      client ! ProcessorSaw(command.id, command.cents)
      context.parent ! ChildCompleted(command.id)
  }
}

// Inside a TestKit-backed specification
val events = TestProbe()(system)
val coordinator =
  TestActorRef[Coordinator](Props[Coordinator]())(system)

coordinator ! Register(events.ref)
coordinator ! Pay("A-17", 12500)

events.expectMsgAllOf[PaymentEvent](
  ProcessorSaw("A-17", 12500),
  PaymentAccepted("A-17")
)

expectMsgAllOf checks for both expected messages without claiming an order between them. ProcessorSaw demonstrates that the child received the exact identifier and amount; PaymentAccepted demonstrates that the coordinator observed completion and published the business outcome. This is stronger than inferring delivery merely because a reply appeared.

Use one probe, or a sequence number from a single producer, when order is contractual. Separate actors and separate probes have no shared global order. Drain a known number of messages with receiveN, and replace Thread.sleep with Akka’s controlled test clock when retry deadlines matter.

Financial examples expose rounding mistakes. GST is 10 percent for most domestic sales, and a price change at the start of the Australian financial year on 1 July can produce separate taxable and exempt events. Keep amounts in integer cents, include tax treatment in the message, and assert cents and tax events instead of formatted dollar strings.

Probe boundaries and failure paths

Keep most tests at the actor boundary. With Classic Akka, spawn through TestKit, send a command, and use a TestProbe as the caller or event sink. That verifies what a client can observe without coupling the test to child names. A TestActorRef can expose internal state or a child reference, but it bypasses the normal mailbox path, so it should not replace an end-to-end message exchange.

Failures need separate assertions. Attach a DeathWatch to the child and use expectTerminated to confirm its stop. Then assert the supervisor received the expected failure event and, where applicable, that a replacement processed the next message. Otherwise a passing test can show an actor disappeared without proving that supervision restored the workflow.

Data handling belongs in the test design. The Privacy Act 1988 (Cth) and its Australian Privacy Principles, including APP 11 on security, make careful handling of personal information an operational concern for Australian organisations. Keep synthetic identifiers in probe messages, and ensure audit events copy no more personal data than the workflow needs. If a legacy actor graph makes ownership ambiguous, Akka architecture consulting can provide a second review of supervision and routing before assertions harden around the wrong structure.

Compare patterns and choose deliberately

The right pattern depends on whether the contract is a boundary outcome, an internal route, an ordering rule or a failure lifecycle. Keep separate probes for separate concerns and make each assertion’s scope obvious from its name. A test that checks only the final response may miss a duplicated internal publish; a test tied to every child name may fail during a harmless refactor.

The commercial context also shapes adoption. Akka 2.7 moved from Apache 2.0 to the Business Source License. Under its published terms, organisations with annual revenue below US$25 million can use Akka without a commercial fee, while higher-revenue organisations need a commercial agreement. That affects deployment cost and client tenders. Teams evaluating Apache Pekko should check actor API differences and keep behavioural assertions portable.

Checks to prioritise in an actor test

A compact suite can separate frequent checks from risk-heavy scenarios.

Routine flow checks

  • Match a typed result with expectMsgType, then assert its fields.
  • Use receiveN when a known number of messages must arrive.
  • Give each expectation a timeout tied to the service contract.

Failure and privacy checks

  • Assert child termination before expecting supervisor recovery.
  • Pair timeout behaviour with a controlled test clock, not a sleep.
  • Check that logs and probe messages exclude unnecessary personal data.

The practical trade-off is coverage against coupling.

Pattern Use it for Main assertion Watch out for
Boundary exchange Public requests and responses probe.expectMsg A final reply hiding lost internal work
Instrumented child Parent-to-child routing Probe-backed child and parent probe Coupling to implementation details
Ordered stream Messages from one producer expectMsg sequence or receiveN Assuming global ordering across actors
Failure lifecycle Stops, restarts and escalation expectTerminated with a supervisor event Termination without successful recovery

Turn one workflow into an executable contract

A good rollout starts with one workflow rather than a whole actor graph. Choose a payment, order or status-update path; name its commands and events; decide which messages are contractual at the boundary and which are internal routing details.

Run the test in the same CI pipeline as the service. A green build proves the current implementation, while the recorded expectations reveal whether a dependency upgrade, supervision change or scheduler adjustment altered the flow. Keep test data synthetic and replace only external adapters.

Add a TestProbe to one working actor interaction today: assert the first externally meaningful outcome, then expand from that verified boundary as the contract grows.


cat ~/interests.json

KeyValue
editorTerminal-first workflow
osMac OS X / Unix
vcsGit, distributed version control
deployCapistrano, cron automation
graphicsSVG, Adobe Illustrator troubleshooting
networkingIP validation, SSH, VPN

git log --oneline --reverse

2013-10-30

Solving SVG import issues in Adobe Illustrator CS6 and CC

When importing an SVG into Illustrator, the operation fails with an unknown error [CANT]. A workaround for this Adobe-side bug.

2013

Solving NDK build issues on OS X

Troubleshooting native development kit compilation problems on Mac OS X.

2013

Programmatically adding PHP generated TypoScript to the backend configuration

Integrating dynamically generated TypoScript into Typo3 backend setups using PHP.

2013

ArgumentError: Could not parse PKey: no start line

Debugging an SSH key parsing error encountered during deployment.

2011-08-04

Validating IP-Addresses in PHP

Using PHP filter functions with flags like FILTER_FLAG_IPV4 and FILTER_FLAG_IPV6, and understanding how filter_var handles reserved IP addresses.

2011-07-09

Cocoa: Using NSTrackingArea

A short tutorial on using Cocoa's NSTrackingArea to capture mouseEntered and mouseExited events.


cat ~/contact.txt

github: github.com/x3ro
stackoverflow: x3ro
coderwall: coderwall.com/x3ro
twitter: @x3rames