or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-testing.mddeterministic-execution.mdevent-filtering.mdfsm-testing.mdindex.mdsynchronization.mdsynchronous-testing.mdtest-utilities.md

synchronous-testing.mddocs/

0

# Synchronous Actor Testing

1

2

TestActorRef provides synchronous access to actor internals for unit testing individual actors without the asynchronous nature of the actor model. This enables direct testing of actor state and behavior through synchronous method calls.

3

4

## Capabilities

5

6

### TestActorRef Class

7

8

Special ActorRef for synchronous testing with direct access to the underlying actor instance.

9

10

```scala { .api }

11

/**

12

* Special ActorRef exclusively for unit testing in single-threaded environment

13

* Overrides dispatcher to CallingThreadDispatcher for synchronous execution

14

* @param _system ActorSystem to use

15

* @param _props Props for creating the actor

16

* @param _supervisor Supervisor actor reference

17

* @param name Name for the test actor

18

*/

19

class TestActorRef[T <: Actor](_system: ActorSystem, _props: Props,

20

_supervisor: ActorRef, name: String)

21

extends LocalActorRef {

22

23

/**

24

* Direct access to underlying actor instance

25

* WARNING: Only use in single-threaded test environments

26

* @return The actor instance

27

*/

28

def underlyingActor: T

29

30

/**

31

* Directly inject message into actor's receive method

32

* @param o Message to inject

33

*/

34

def receive(o: Any): Unit

35

36

/**

37

* Directly inject message with specific sender

38

* @param o Message to inject

39

* @param sender Sender reference for the message

40

*/

41

def receive(o: Any, sender: ActorRef): Unit

42

43

/**

44

* Watch subject actor for termination

45

* @param subject Actor to watch

46

* @return The subject ActorRef

47

*/

48

def watch(subject: ActorRef): ActorRef

49

50

/**

51

* Stop watching subject actor

52

* @param subject Actor to unwatch

53

* @return The subject ActorRef

54

*/

55

def unwatch(subject: ActorRef): ActorRef

56

}

57

```

58

59

### Factory Methods

60

61

Factory methods for creating TestActorRef instances with various parameter combinations.

62

63

```scala { .api }

64

object TestActorRef {

65

/**

66

* Create TestActorRef from actor factory function

67

* @param factory Function that creates actor instance

68

* @param system Implicit ActorSystem

69

* @return TestActorRef for the created actor

70

*/

71

def apply[T <: Actor: ClassTag](factory: => T)

72

(implicit system: ActorSystem): TestActorRef[T]

73

74

/**

75

* Create named TestActorRef from actor factory function

76

* @param factory Function that creates actor instance

77

* @param name Name for the actor

78

* @param system Implicit ActorSystem

79

* @return TestActorRef for the created actor

80

*/

81

def apply[T <: Actor: ClassTag](factory: => T, name: String)

82

(implicit system: ActorSystem): TestActorRef[T]

83

84

/**

85

* Create TestActorRef from Props

86

* @param props Props for creating the actor

87

* @param system Implicit ActorSystem

88

* @return TestActorRef for the created actor

89

*/

90

def apply[T <: Actor](props: Props)

91

(implicit system: ActorSystem): TestActorRef[T]

92

93

/**

94

* Create named TestActorRef from Props

95

* @param props Props for creating the actor

96

* @param name Name for the actor

97

* @param system Implicit ActorSystem

98

* @return TestActorRef for the created actor

99

*/

100

def apply[T <: Actor](props: Props, name: String)

101

(implicit system: ActorSystem): TestActorRef[T]

102

103

/**

104

* Create TestActorRef with custom supervisor from Props

105

* @param props Props for creating the actor

106

* @param supervisor Supervisor actor reference

107

* @param name Name for the actor

108

* @param system Implicit ActorSystem

109

* @return TestActorRef for the created actor

110

*/

111

def apply[T <: Actor](props: Props, supervisor: ActorRef, name: String)

112

(implicit system: ActorSystem): TestActorRef[T]

113

}

114

```

115

116

### Java API

117

118

Java-friendly factory methods for creating TestActorRef instances.

119

120

```scala { .api }

121

object TestActorRef {

122

/**

123

* Java API: Create TestActorRef from Props

124

* @param system ActorSystem to use

125

* @param props Props for creating the actor

126

* @return TestActorRef for the created actor

127

*/

128

def create[T <: Actor](system: ActorSystem, props: Props): TestActorRef[T]

129

130

/**

131

* Java API: Create named TestActorRef from Props

132

* @param system ActorSystem to use

133

* @param props Props for creating the actor

134

* @param name Name for the actor

135

* @return TestActorRef for the created actor

136

*/

137

def create[T <: Actor](system: ActorSystem, props: Props,

138

name: String): TestActorRef[T]

139

140

/**

141

* Java API: Create TestActorRef with custom supervisor

142

* @param system ActorSystem to use

143

* @param props Props for creating the actor

144

* @param supervisor Supervisor actor reference

145

* @param name Name for the actor

146

* @return TestActorRef for the created actor

147

*/

148

def create[T <: Actor](system: ActorSystem, props: Props,

149

supervisor: ActorRef, name: String): TestActorRef[T]

150

}

151

```

152

153

**Usage Examples:**

154

155

```scala

156

import akka.actor.{Actor, ActorSystem, Props}

157

import akka.testkit.TestActorRef

158

159

class CounterActor extends Actor {

160

private var count = 0

161

162

def receive = {

163

case "increment" => count += 1

164

case "get" => sender() ! count

165

case "reset" => count = 0

166

}

167

168

def getCount: Int = count

169

}

170

171

class TestActorRefExample {

172

implicit val system = ActorSystem("test")

173

174

// Create TestActorRef from Props

175

val counterRef = TestActorRef[CounterActor](Props[CounterActor])

176

177

// Direct access to actor instance

178

val counter = counterRef.underlyingActor

179

180

// Synchronous message injection

181

counterRef.receive("increment")

182

counterRef.receive("increment")

183

184

// Direct state inspection

185

assert(counter.getCount == 2)

186

187

// Test with specific sender

188

val probe = TestProbe()

189

counterRef.receive("get", probe.ref)

190

probe.expectMsg(2)

191

192

// Factory method with name

193

val namedRef = TestActorRef[CounterActor](Props[CounterActor], "my-counter")

194

195

// Factory method with actor factory

196

val factoryRef = TestActorRef(new CounterActor)

197

}

198

```

199

200

### Integration with Regular ActorRef

201

202

TestActorRef extends ActorRef so it can be used anywhere a regular ActorRef is expected, but provides additional synchronous testing capabilities.

203

204

```scala

205

// TestActorRef can be used as regular ActorRef

206

val testRef: ActorRef = TestActorRef[CounterActor](Props[CounterActor])

207

208

// Send messages asynchronously (normal actor behavior)

209

testRef ! "increment"

210

211

// But also supports synchronous testing

212

val testActorRef = testRef.asInstanceOf[TestActorRef[CounterActor]]

213

testActorRef.receive("increment") // Synchronous

214

val actor = testActorRef.underlyingActor // Direct access

215

```

216

217

### Thread Safety Considerations

218

219

TestActorRef is designed for single-threaded testing environments and should not be used in multi-threaded scenarios.

220

221

```scala

222

// SAFE: Single-threaded test

223

class SingleThreadedTest extends TestKit(ActorSystem("test")) {

224

val ref = TestActorRef[MyActor](Props[MyActor])

225

ref.receive("message")

226

val state = ref.underlyingActor.someState

227

}

228

229

// UNSAFE: Multi-threaded access

230

// Don't access underlyingActor from multiple threads

231

// Don't mix asynchronous ! with synchronous receive()

232

```

233

234

### Supervision and Lifecycle

235

236

TestActorRef supports standard actor lifecycle events and supervision, but executes them synchronously.

237

238

```scala

239

class SupervisedActor extends Actor {

240

def receive = {

241

case "fail" => throw new RuntimeException("Test failure")

242

case msg => // handle normally

243

}

244

245

override def preStart(): Unit = println("Starting")

246

override def postStop(): Unit = println("Stopping")

247

}

248

249

// Supervision works synchronously

250

val ref = TestActorRef[SupervisedActor](Props[SupervisedActor])

251

252

// Lifecycle methods are called synchronously

253

// preStart() already called during construction

254

255

// Exception handling is synchronous

256

try {

257

ref.receive("fail")

258

} catch {

259

case _: RuntimeException => // Handle test exception

260

}

261

```