Unit testing framework for isolated behavior testing with comprehensive effect capture and assertion capabilities. Uses BehaviorTestKit for deterministic testing without real actor systems.
Main API for synchronous behavior testing providing isolated execution and effect capture.
/**
* Factory methods for creating BehaviorTestKit instances
*/
object BehaviorTestKit {
/** Create with default name "testkit" */
def apply[T](initialBehavior: Behavior[T]): BehaviorTestKit[T]
/** Create with custom name */
def apply[T](initialBehavior: Behavior[T], name: String): BehaviorTestKit[T]
/** Create with custom name and configuration */
def apply[T](initialBehavior: Behavior[T], name: String, config: Config): BehaviorTestKit[T]
/** Default test configuration */
val ApplicationTestConfig: Config
}
/**
* TestKit for synchronous behavior testing and effect verification
*/
trait BehaviorTestKit[T] {
/** Send message to behavior and record effects */
def run(message: T): Unit
/** Process first message in self inbox */
def runOne(): Unit
/** Send signal to behavior and record effects */
def signal(signal: Signal): Unit
/** Test ask patterns with reply capture */
def runAsk[Res](f: ActorRef[Res] => T): ReplyInbox[Res]
/** Test status reply ask patterns */
def runAskWithStatus[Res](f: ActorRef[StatusReply[Res]] => T): StatusReplyInbox[Res]
/** Get oldest effect (consumes it) */
def retrieveEffect(): Effect
/** Get all effects (consumes them) */
def retrieveAllEffects(): immutable.Seq[Effect]
/** Check if effects exist */
def hasEffects(): Boolean
/** Assert specific effect and consume it */
def expectEffect(expectedEffect: Effect): Unit
/** Assert effect type and return it */
def expectEffectType[E <: Effect](implicit classTag: ClassTag[E]): E
/** Assert effect with partial function */
def expectEffectPF[R](f: PartialFunction[Effect, R]): R
/** Get child inbox by name */
def childInbox[U](name: String): TestInbox[U]
/** Get child inbox by ActorRef */
def childInbox[U](child: ActorRef[U]): TestInbox[U]
/** Get nested BehaviorTestKit for child */
def childTestKit[U](child: ActorRef[U]): BehaviorTestKit[U]
/** Get self message inbox */
def selfInbox(): TestInbox[T]
/** Get self actor reference */
def ref: ActorRef[T]
/** Get current behavior */
def currentBehavior: Behavior[T]
/** Get behavior returned from last message processing */
def returnedBehavior: Behavior[T]
/** Check if behavior is alive */
def isAlive: Boolean
/** Get captured log events */
def logEntries(): immutable.Seq[CapturedLogEvent]
/** Clear captured log events */
def clearLog(): Unit
/** Get receptionist inbox */
def receptionistInbox(): TestInbox[Receptionist.Command]
}Usage Examples:
import akka.actor.testkit.typed.scaladsl.BehaviorTestKit
import akka.actor.testkit.typed.Effect
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.{ActorRef, Behavior}
// Define a behavior that spawns a child
def parentBehavior(): Behavior[String] = Behaviors.setup { context =>
Behaviors.receiveMessage {
case "spawn-child" =>
context.spawn(childBehavior(), "child")
Behaviors.same
case msg =>
println(s"Parent received: $msg")
Behaviors.same
}
}
def childBehavior(): Behavior[String] = Behaviors.receiveMessage { msg =>
println(s"Child received: $msg")
Behaviors.same
}
// Create test kit
val testKit = BehaviorTestKit(parentBehavior())
// Test spawning behavior
testKit.run("spawn-child")
// Verify spawn effect
val spawnEffect = testKit.expectEffectType[Effect.Spawned[String]]
assert(spawnEffect.name == "child")
// Get child inbox and test it
val childInbox = testKit.childInbox[String]("child")
childInbox.ref ! "hello child"
assert(childInbox.receiveMessage() == "hello child")Core functionality for sending messages to behaviors and capturing the resulting effects.
/**
* Send message to behavior and record any effects
* @param message Message to send to the behavior
*/
def run(message: T): Unit
/**
* Process first message in self inbox and record effects
*/
def runOne(): Unit
/**
* Send signal to behavior and record effects
* @param signal Signal to send (e.g., PostStop, PreRestart)
*/
def signal(signal: Signal): Unit
/**
* Test ask pattern by constructing message with reply-to ActorRef
* @param f Function that creates message using provided reply-to ref
* @returns ReplyInbox for capturing the reply
*/
def runAsk[Res](f: ActorRef[Res] => T): ReplyInbox[Res]
/**
* Test ask pattern for StatusReply responses
* @param f Function that creates message using provided reply-to ref
* @returns StatusReplyInbox for capturing status reply
*/
def runAskWithStatus[Res](f: ActorRef[StatusReply[Res]] => T): StatusReplyInbox[Res]Methods for inspecting and asserting on the effects produced by behavior execution.
/**
* Get the oldest effect and remove it from further assertions
* @returns The oldest Effect or Effect.NoEffects if none
*/
def retrieveEffect(): Effect
/**
* Get all current effects and clear them
* @returns All current effects as a sequence
*/
def retrieveAllEffects(): immutable.Seq[Effect]
/**
* Check if any effects are currently recorded
* @returns true if effects exist
*/
def hasEffects(): Boolean
/**
* Assert that the oldest effect matches the expected effect
* @param expectedEffect The effect to match against
*/
def expectEffect(expectedEffect: Effect): Unit
/**
* Assert that the oldest effect is of the specified type
* @tparam E The effect type to expect
* @returns The matched effect for further inspection
*/
def expectEffectType[E <: Effect](implicit classTag: ClassTag[E]): E
/**
* Assert that the oldest effect matches the partial function
* @param f Partial function to match against the effect
* @returns Result of applying the partial function
*/
def expectEffectPF[R](f: PartialFunction[Effect, R]): RUsage Examples:
import akka.actor.testkit.typed.scaladsl.BehaviorTestKit
import akka.actor.testkit.typed.Effect
import akka.actor.typed.scaladsl.Behaviors
// Behavior that watches another actor
def watcherBehavior(target: ActorRef[String]): Behavior[String] =
Behaviors.setup { context =>
context.watch(target)
Behaviors.receiveMessage { _ => Behaviors.same }
}
val probe = TestProbe[String]()
val testKit = BehaviorTestKit(watcherBehavior(probe.ref))
// Verify watch effect
val watchEffect = testKit.expectEffectType[Effect.Watched[String]]
assert(watchEffect.other == probe.ref)
// Test with partial function
testKit.expectEffectPF {
case Effect.Watched(actor) => actor.path.name
} // Returns the actor name
// Check for no effects
testKit.run("some message")
testKit.expectEffect(Effect.NoEffects)Utilities for testing behaviors that create and interact with child actors.
/**
* Get test inbox for child actor by name
* @param name Name of the child actor
* @tparam U Message type of the child
* @returns TestInbox for the child
*/
def childInbox[U](name: String): TestInbox[U]
/**
* Get test inbox for child actor by ActorRef
* @param child ActorRef of the child
* @tparam U Message type of the child
* @returns TestInbox for the child
*/
def childInbox[U](child: ActorRef[U]): TestInbox[U]
/**
* Get nested BehaviorTestKit for testing child behavior
* @param child ActorRef of the child
* @tparam U Message type of the child
* @returns BehaviorTestKit for the child
*/
def childTestKit[U](child: ActorRef[U]): BehaviorTestKit[U]Methods for inspecting the current state and lifecycle of the behavior under test.
/**
* Get the current active behavior
* @returns Current Behavior instance
*/
def currentBehavior: Behavior[T]
/**
* Get the behavior as returned from processing the previous message
* @returns Returned Behavior (may be Behaviors.unhandled, etc.)
*/
def returnedBehavior: Behavior[T]
/**
* Check if the behavior is still alive (not stopped)
* @returns true if behavior is alive
*/
def isAlive: Boolean
/**
* Get the self ActorRef for the behavior
* @returns ActorRef representing the behavior
*/
def ref: ActorRef[T]
/**
* Get the self message inbox containing messages sent to context.self
* @returns TestInbox for self messages
*/
def selfInbox(): TestInbox[T]Utilities for capturing logs and accessing system-level message inboxes.
/**
* Get all captured log events from the behavior
* @returns Sequence of captured log events with metadata
*/
def logEntries(): immutable.Seq[CapturedLogEvent]
/**
* Clear all captured log events
*/
def clearLog(): Unit
/**
* Get inbox for receptionist commands sent to system.receptionist
* @returns TestInbox for Receptionist.Command messages
*/
def receptionistInbox(): TestInbox[Receptionist.Command]