Specialized actor references that provide direct access to actor internals and state, enabling unit testing of actor behavior without message passing.
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)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"))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)
}
}TestActorRef characteristics:
TestFSMRef additional features:
When to use: