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

fsm-testing.mddocs/

0

# FSM Testing

1

2

TestFSMRef provides specialized testing utilities for Finite State Machine actors, extending TestActorRef with FSM-specific functionality including state inspection, state manipulation, and timer management for comprehensive FSM testing.

3

4

## Capabilities

5

6

### TestFSMRef Class

7

8

Specialized TestActorRef for testing FSM (Finite State Machine) actors with state inspection and timer management.

9

10

```scala { .api }

11

/**

12

* Specialized TestActorRef for FSM actors

13

* Provides direct access to FSM state and timer management

14

* @param system ActorSystem to use

15

* @param props Props for creating the FSM actor

16

* @param supervisor Supervisor actor reference

17

* @param name Name for the test actor

18

*/

19

class TestFSMRef[S, D, T <: Actor](system: ActorSystem, props: Props,

20

supervisor: ActorRef, name: String)

21

extends TestActorRef[T] {

22

23

/**

24

* Get current FSM state name

25

* @return Current state name of type S

26

*/

27

def stateName: S

28

29

/**

30

* Get current FSM state data

31

* @return Current state data of type D

32

*/

33

def stateData: D

34

35

/**

36

* Manually set FSM state (for testing state transitions)

37

* @param stateName New state name

38

* @param stateData New state data

39

*/

40

def setState(stateName: S, stateData: D): Unit

41

42

/**

43

* Set FSM state with timeout and stop reason

44

* @param stateName New state name

45

* @param stateData New state data

46

* @param timeout State timeout duration

47

* @param stopReason Optional stop reason

48

*/

49

def setState(stateName: S, stateData: D, timeout: FiniteDuration,

50

stopReason: Option[FSM.Reason]): Unit

51

}

52

```

53

54

### Timer Management

55

56

Methods for managing FSM timers including starting, canceling, and checking timer status.

57

58

```scala { .api }

59

/**

60

* Start timer with fixed delay between executions

61

* @param name Timer name identifier

62

* @param msg Message to send when timer fires

63

* @param delay Delay between timer executions

64

*/

65

def startTimerWithFixedDelay(name: String, msg: Any, delay: FiniteDuration): Unit

66

67

/**

68

* Start timer with fixed rate execution

69

* @param name Timer name identifier

70

* @param msg Message to send when timer fires

71

* @param interval Fixed interval between timer executions

72

*/

73

def startTimerAtFixedRate(name: String, msg: Any, interval: FiniteDuration): Unit

74

75

/**

76

* Start single-shot timer

77

* @param name Timer name identifier

78

* @param msg Message to send when timer fires

79

* @param delay Delay before timer fires once

80

*/

81

def startSingleTimer(name: String, msg: Any, delay: FiniteDuration): Unit

82

83

/**

84

* Cancel named timer

85

* @param name Timer name to cancel

86

*/

87

def cancelTimer(name: String): Unit

88

89

/**

90

* Check if named timer is active

91

* @param name Timer name to check

92

* @return True if timer is active, false otherwise

93

*/

94

def isTimerActive(name: String): Boolean

95

96

/**

97

* Check if state timeout timer is active

98

* @return True if state timeout timer is active

99

*/

100

def isStateTimerActive: Boolean

101

```

102

103

### Factory Methods

104

105

Factory methods for creating TestFSMRef instances with various parameter combinations.

106

107

```scala { .api }

108

object TestFSMRef {

109

/**

110

* Create TestFSMRef from actor factory function

111

* @param factory Function that creates FSM actor instance

112

* @param ev Evidence that T is an FSM[S, D]

113

* @param system Implicit ActorSystem

114

* @return TestFSMRef for the created FSM actor

115

*/

116

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

117

(implicit ev: T <:< FSM[S, D],

118

system: ActorSystem): TestFSMRef[S, D, T]

119

120

/**

121

* Create named TestFSMRef from actor factory function

122

* @param factory Function that creates FSM actor instance

123

* @param name Name for the actor

124

* @param ev Evidence that T is an FSM[S, D]

125

* @param system Implicit ActorSystem

126

* @return TestFSMRef for the created FSM actor

127

*/

128

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

129

(implicit ev: T <:< FSM[S, D],

130

system: ActorSystem): TestFSMRef[S, D, T]

131

132

/**

133

* Create TestFSMRef from Props

134

* @param props Props for creating the FSM actor

135

* @param ev Evidence that T is an FSM[S, D]

136

* @param system Implicit ActorSystem

137

* @return TestFSMRef for the created FSM actor

138

*/

139

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

140

(implicit ev: T <:< FSM[S, D],

141

system: ActorSystem): TestFSMRef[S, D, T]

142

143

/**

144

* Create named TestFSMRef from Props

145

* @param props Props for creating the FSM actor

146

* @param name Name for the actor

147

* @param supervisor Optional supervisor reference

148

* @param ev Evidence that T is an FSM[S, D]

149

* @param system Implicit ActorSystem

150

* @return TestFSMRef for the created FSM actor

151

*/

152

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

153

supervisor: Option[ActorRef] = None)

154

(implicit ev: T <:< FSM[S, D],

155

system: ActorSystem): TestFSMRef[S, D, T]

156

}

157

```

158

159

**Usage Examples:**

160

161

```scala

162

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

163

import akka.testkit.{TestFSMRef, TestKit}

164

import scala.concurrent.duration._

165

166

// Example FSM states and data

167

sealed trait State

168

case object Idle extends State

169

case object Active extends State

170

case object Completed extends State

171

172

case class Data(count: Int, items: List[String])

173

174

// Example FSM Actor

175

class ProcessorFSM extends FSM[State, Data] {

176

startWith(Idle, Data(0, Nil))

177

178

when(Idle) {

179

case Event("start", data) =>

180

goto(Active) using data.copy(count = 1)

181

}

182

183

when(Active, stateTimeout = 5.seconds) {

184

case Event("process", data) =>

185

val newData = data.copy(count = data.count + 1)

186

if (newData.count >= 10) {

187

goto(Completed) using newData

188

} else {

189

stay() using newData

190

}

191

case Event(StateTimeout, data) =>

192

goto(Idle) using data.copy(count = 0)

193

}

194

195

when(Completed) {

196

case Event("reset", _) =>

197

goto(Idle) using Data(0, Nil)

198

}

199

}

200

201

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

202

203

"ProcessorFSM" should {

204

"start in Idle state" in {

205

val fsmRef = TestFSMRef(new ProcessorFSM)

206

207

// Check initial state

208

assert(fsmRef.stateName == Idle)

209

assert(fsmRef.stateData == Data(0, Nil))

210

}

211

212

"transition from Idle to Active" in {

213

val fsmRef = TestFSMRef(new ProcessorFSM)

214

215

// Send transition message

216

fsmRef ! "start"

217

218

// Verify state change

219

assert(fsmRef.stateName == Active)

220

assert(fsmRef.stateData.count == 1)

221

}

222

223

"handle state timeout" in {

224

val fsmRef = TestFSMRef(new ProcessorFSM)

225

226

// Manually set state to Active

227

fsmRef.setState(Active, Data(5, Nil))

228

229

// Check state timeout timer

230

assert(fsmRef.isStateTimerActive)

231

232

// Trigger state timeout

233

fsmRef ! FSM.StateTimeout

234

235

// Verify transition back to Idle

236

assert(fsmRef.stateName == Idle)

237

assert(fsmRef.stateData.count == 0)

238

}

239

240

"manage custom timers" in {

241

val fsmRef = TestFSMRef(new ProcessorFSM)

242

243

// Start a custom timer

244

fsmRef.startSingleTimer("reminder", "remind", 1.second)

245

246

// Check timer status

247

assert(fsmRef.isTimerActive("reminder"))

248

249

// Cancel timer

250

fsmRef.cancelTimer("reminder")

251

assert(!fsmRef.isTimerActive("reminder"))

252

}

253

254

"support manual state manipulation" in {

255

val fsmRef = TestFSMRef(new ProcessorFSM)

256

257

// Manually set complex state for testing

258

val testData = Data(8, List("item1", "item2"))

259

fsmRef.setState(Active, testData)

260

261

// Verify manual state setting

262

assert(fsmRef.stateName == Active)

263

assert(fsmRef.stateData == testData)

264

265

// Continue with normal FSM behavior

266

fsmRef ! "process"

267

fsmRef ! "process"

268

269

// Should transition to Completed (count reaches 10)

270

assert(fsmRef.stateName == Completed)

271

assert(fsmRef.stateData.count == 10)

272

}

273

}

274

}

275

```

276

277

### FSM Integration Features

278

279

TestFSMRef integrates with Akka FSM features for comprehensive testing.

280

281

```scala

282

// Testing FSM with complex data transformations

283

class DataProcessorFSM extends FSM[ProcessState, ProcessData] {

284

when(Processing) {

285

case Event(ProcessItem(item), data) =>

286

val newData = data.addItem(item)

287

if (newData.isComplete) {

288

goto(Complete) using newData forMax 30.seconds

289

} else {

290

stay() using newData

291

}

292

}

293

}

294

295

// Test complex FSM scenarios

296

val fsmRef = TestFSMRef(new DataProcessorFSM)

297

298

// Set up specific test scenario

299

fsmRef.setState(Processing, ProcessData.withItems(5))

300

301

// Test transition logic

302

fsmRef ! ProcessItem("final-item")

303

assert(fsmRef.stateName == Complete)

304

305

// Test timer functionality

306

assert(fsmRef.isStateTimerActive)

307

fsmRef.startTimerWithFixedDelay("heartbeat", "ping", 100.millis)

308

assert(fsmRef.isTimerActive("heartbeat"))

309

```

310

311

### State Inspection and Debugging

312

313

TestFSMRef provides detailed state inspection for debugging FSM behavior.

314

315

```scala

316

val fsmRef = TestFSMRef(new ComplexFSM)

317

318

// Inspect current state at any time

319

println(s"Current state: ${fsmRef.stateName}")

320

println(s"Current data: ${fsmRef.stateData}")

321

322

// Check timer status for debugging

323

println(s"State timer active: ${fsmRef.isStateTimerActive}")

324

println(s"Custom timer active: ${fsmRef.isTimerActive("custom")}")

325

326

// Set breakpoints in state for step-by-step testing

327

fsmRef.setState(CriticalState, criticalData)

328

// ... perform tests on critical state

329

```

330

331

### Type Safety

332

333

TestFSMRef maintains full type safety for FSM state names and data types.

334

335

```scala

336

// Type parameters ensure compile-time safety

337

val fsmRef: TestFSMRef[MyState, MyData, MyFSMActor] =

338

TestFSMRef(new MyFSMActor)

339

340

// State name is strongly typed

341

val currentState: MyState = fsmRef.stateName

342

343

// State data is strongly typed

344

val currentData: MyData = fsmRef.stateData

345

346

// setState requires correct types

347

fsmRef.setState(ValidState, validData) // ✓ Compiles

348

// fsmRef.setState("invalid", wrongData) // ✗ Compile error

349

```