or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

actor-refs.mdconfiguration.mdcore-testing.mddispatchers.mdevent-filtering.mdindex.mdjava-dsl.mdpackage-functions.mdsynchronization.mdtest-actors.mdutilities.md
tile.json

actor-refs.mddocs/

Actor References and Direct Testing

Specialized actor references that provide direct access to actor internals and state, enabling unit testing of actor behavior without message passing.

Capabilities

TestActorRef Class

Special ActorRef for unit testing with direct access to underlying actor.

class TestActorRef[T <: Actor] private[akka] (
  val actorRef: InternalActorRef,
  system: ActorSystemImpl,
  props: Props,
  dispatcher: MessageDispatcher,
  supervisor: InternalActorRef
) extends LocalActorRef(system, props, dispatcher, mailboxType, supervisor, actorRef.path) {

  // Direct actor access
  def underlyingActor: T
  
  // Direct message injection
  def receive(o: Any): Unit
  def receive(o: Any, sender: ActorRef): Unit
  
  // State checking
  def isTerminated: Boolean
  
  // All ActorRef methods inherited
  def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit
  def tell(msg: Any, sender: ActorRef): Unit
  def ask(message: Any)(implicit timeout: Timeout): Future[Any]
  def forward(message: Any)(implicit context: ActorContext): Unit
}

Factory Methods:

object TestActorRef {
  // Java API - create methods with explicit ActorSystem
  def create[T <: Actor](system: ActorSystem, props: Props): TestActorRef[T]
  def create[T <: Actor](system: ActorSystem, props: Props, name: String): TestActorRef[T]
  def create[T <: Actor](system: ActorSystem, props: Props, supervisor: ActorRef): TestActorRef[T]
  def create[T <: Actor](system: ActorSystem, props: Props, supervisor: ActorRef, name: String): TestActorRef[T]
  
  // Scala API - apply methods with implicit ActorSystem
  def apply[T <: Actor: ClassTag](factory: => T)(implicit system: ActorSystem): TestActorRef[T]
  def apply[T <: Actor: ClassTag](factory: => T, name: String)(implicit system: ActorSystem): TestActorRef[T]
  def apply[T <: Actor](props: Props)(implicit system: ActorSystem): TestActorRef[T]
  def apply[T <: Actor](props: Props, name: String)(implicit system: ActorSystem): TestActorRef[T]
  def apply[T <: Actor](props: Props, supervisor: ActorRef)(implicit system: ActorSystem): TestActorRef[T]
  def apply[T <: Actor](props: Props, supervisor: ActorRef, name: String)(implicit system: ActorSystem): TestActorRef[T]
  def apply[T <: Actor](implicit t: ClassTag[T], system: ActorSystem): TestActorRef[T]
  def apply[T <: Actor](name: String)(implicit t: ClassTag[T], system: ActorSystem): TestActorRef[T]
  def apply[T <: Actor](supervisor: ActorRef)(implicit t: ClassTag[T], system: ActorSystem): TestActorRef[T]
  def apply[T <: Actor](supervisor: ActorRef, name: String)(implicit t: ClassTag[T], system: ActorSystem): TestActorRef[T]
}

Usage Example:

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

// Define actor to test
class CounterActor extends Actor {
  private var count = 0
  
  def receive = {
    case "inc" => count += 1
    case "get" => sender() ! count
    case "reset" => count = 0
  }
  
  def getCount: Int = count
}

// Unit testing with direct access
val actorRef = TestActorRef[CounterActor](Props[CounterActor]())

// Access underlying actor directly
val actor = actorRef.underlyingActor
assert(actor.getCount == 0)

// Inject messages directly (synchronous)
actorRef.receive("inc")
actorRef.receive("inc")
assert(actor.getCount == 2)

// Test with specific sender
val probe = TestProbe()
actorRef.receive("get", probe.ref)
probe.expectMsg(2)

// Still works as normal ActorRef
actorRef ! "inc"
actorRef ! "get"
expectMsg(3)

TestFSMRef Class

Specialized TestActorRef for testing FSM (Finite State Machine) actors.

class TestFSMRef[S, D, T <: Actor] private[akka] (
  actorRef: InternalActorRef,
  system: ActorSystemImpl, 
  props: Props,
  dispatcher: MessageDispatcher,
  supervisor: InternalActorRef
) extends TestActorRef[T](actorRef, system, props, dispatcher, supervisor) {

  // FSM state access
  def stateName: S
  def stateData: D
  
  // FSM state manipulation
  def setState(stateName: S): Unit
  def setState(stateName: S, stateData: D): Unit
  def setState(stateName: S, stateData: D, timeout: FiniteDuration): Unit
  def setState(stateName: S, stateData: D, timeout: FiniteDuration, reason: FSM.Reason): Unit
  
  // Timer management
  def startTimerWithFixedDelay(name: String, msg: Any, delay: FiniteDuration): Unit
  def startTimerAtFixedRate(name: String, msg: Any, interval: FiniteDuration): Unit
  def startSingleTimer(name: String, msg: Any, delay: FiniteDuration): Unit
  def cancelTimer(name: String): Unit
  def isTimerActive(name: String): Boolean
  
  // FSM monitoring
  def isStateTimerActive: Boolean
  def setStateTimeout(state: S, timeout: FiniteDuration): Unit
}

Factory Methods:

object TestFSMRef {
  def create[S, D, T <: Actor](factory: => T, name: String)(implicit system: ActorSystem): TestFSMRef[S, D, T]
  def create[S, D, T <: Actor](factory: => T)(implicit system: ActorSystem): TestFSMRef[S, D, T]
  
  def apply[S, D, T <: Actor](factory: => T, name: String)(implicit system: ActorSystem): TestFSMRef[S, D, T]
  def apply[S, D, T <: Actor](factory: => T)(implicit system: ActorSystem): TestFSMRef[S, D, T]
}

Usage Example:

import akka.actor.{FSM, Props}
import akka.testkit.TestFSMRef
import scala.concurrent.duration._

// FSM states and data
sealed trait State
case object Idle extends State
case object Active extends State

case class Data(count: Int)

// FSM Actor
class MyFSM extends FSM[State, Data] {
  startWith(Idle, Data(0))
  
  when(Idle) {
    case Event("start", d) => goto(Active) using d.copy(count = d.count + 1)
  }
  
  when(Active, stateTimeout = 5.seconds) {
    case Event("work", d) => stay using d.copy(count = d.count + 1)
    case Event(StateTimeout, d) => goto(Idle)
  }
}

// Testing FSM
val fsmRef = TestFSMRef[State, Data, MyFSM](new MyFSM)

// Check initial state
assert(fsmRef.stateName == Idle)
assert(fsmRef.stateData == Data(0))

// Send message and verify state change
fsmRef ! "start"
assert(fsmRef.stateName == Active)
assert(fsmRef.stateData == Data(1))

// Manually set state for testing
fsmRef.setState(Idle, Data(10))
assert(fsmRef.stateName == Idle)
assert(fsmRef.stateData == Data(10))

// Test timers
fsmRef.startSingleTimer("test", "tick", 1.second)
assert(fsmRef.isTimerActive("test"))

fsmRef.cancelTimer("test")
assert(!fsmRef.isTimerActive("test"))

Integration with Testing

Both TestActorRef and TestFSMRef can be used within TestKit for comprehensive testing:

class ActorIntegrationTest extends TestKit(ActorSystem("TestSystem")) with ImplicitSender {
  
  "TestActorRef integration" in {
    val actorRef = TestActorRef[MyActor](Props[MyActor]())
    
    // Direct testing
    val actor = actorRef.underlyingActor
    
    // Combined with message testing
    actorRef ! "hello"
    expectMsg("hello")
    
    // Verify internal state
    assert(actor.internalState == expectedState)
  }
  
  "TestFSMRef integration" in {
    val fsmRef = TestFSMRef[State, Data, MyFSM](new MyFSM)
    
    // Test state transitions
    fsmRef ! "start"
    assert(fsmRef.stateName == Active)
    
    // Test with expectations
    fsmRef ! "work"
    expectNoMessage(100.millis) // FSM doesn't reply
    assert(fsmRef.stateData.count == 2)
  }
}

Differences from Regular ActorRef

TestActorRef characteristics:

  • Synchronous message processing by default
  • Direct access to actor instance
  • No mailbox queuing (messages processed immediately)
  • Same thread execution (unless using CallingThreadDispatcher)
  • Ideal for unit testing individual actors

TestFSMRef additional features:

  • FSM state inspection and manipulation
  • Timer management and testing
  • State timeout testing
  • FSM-specific assertions and verifications

When to use:

  • TestActorRef: Unit testing individual actor logic, verifying internal state changes
  • TestFSMRef: Testing finite state machines, complex state transitions, timer-based behavior
  • Regular TestProbe: Integration testing, actor interaction testing, asynchronous behavior testing