0
# Configuration and Extensions
1
2
TestKit configuration system and Akka extensions for customizing test behavior, timeouts, and environment settings.
3
4
## Capabilities
5
6
### TestKitExtension
7
8
Akka extension for accessing TestKit settings and configuration.
9
10
```scala { .api }
11
object TestKitExtension extends ExtensionId[TestKitSettings] with ExtensionIdProvider {
12
def apply(system: ActorSystem): TestKitSettings
13
def apply(system: ExtendedActorSystem): TestKitSettings
14
def createExtension(system: ExtendedActorSystem): TestKitSettings
15
def lookup(): ExtensionId[_ <: Extension] = TestKitExtension
16
}
17
```
18
19
### TestKitSettings
20
21
Configuration settings class for TestKit behavior and timeouts.
22
23
```scala { .api }
24
class TestKitSettings(val config: Config) extends Extension {
25
import TestKitSettings._
26
27
// Core timing settings
28
val TestTimeFactor: Double
29
val SingleExpectDefaultTimeout: FiniteDuration
30
val MultiExpectDefaultTimeout: FiniteDuration
31
val ExpectNoMessageDefaultTimeout: FiniteDuration
32
val TestEventFilterLeeway: FiniteDuration
33
val DefaultTimeout: Timeout
34
35
// Utility methods
36
def dilated(duration: FiniteDuration): FiniteDuration
37
}
38
39
object TestKitSettings {
40
def apply(system: ActorSystem): TestKitSettings = TestKitExtension(system)
41
}
42
```
43
44
## Configuration Reference
45
46
### Default Configuration Values
47
48
```hocon
49
akka.test {
50
# Time factor for dilating timeouts in tests
51
# Set higher for slow test environments (CI, etc.)
52
timefactor = 1.0
53
54
# Default timeout for single message expectations
55
single-expect-default = 3s
56
57
# Default timeout for multi-message expectations
58
multi-expect-default = 3s
59
60
# Default timeout for expectNoMessage calls
61
expect-no-message-default = 100ms
62
63
# Extra time to wait for expected log events in EventFilter
64
filter-leeway = 3s
65
66
# Default dispatcher for test actors
67
default-timeout = 5s
68
}
69
```
70
71
### Usage Examples
72
73
**Accessing Configuration:**
74
75
```scala
76
import akka.testkit.{TestKit, TestKitExtension}
77
78
class ConfigurationTest extends TestKit(ActorSystem("TestSystem")) {
79
val settings = TestKitExtension(system)
80
81
"TestKit configuration" should {
82
"provide access to timing settings" in {
83
settings.TestTimeFactor should be >= 1.0
84
settings.SingleExpectDefaultTimeout should be > Duration.Zero
85
settings.MultiExpectDefaultTimeout should be > Duration.Zero
86
settings.ExpectNoMessageDefaultTimeout should be > Duration.Zero
87
settings.TestEventFilterLeeway should be > Duration.Zero
88
settings.DefaultTimeout.duration should be > Duration.Zero
89
}
90
91
"dilate durations correctly" in {
92
val original = 1.second
93
val dilated = settings.dilated(original)
94
95
dilated should be >= original
96
dilated should equal(Duration.fromNanos((original.toNanos * settings.TestTimeFactor).toLong))
97
}
98
99
"be accessible through extension" in {
100
val settingsViaExtension = TestKitExtension(system)
101
val settingsViaApply = TestKitSettings(system)
102
103
settingsViaExtension should be theSameInstanceAs settingsViaApply
104
}
105
}
106
}
107
```
108
109
### Custom Configuration
110
111
**Override Default Values:**
112
113
```scala
114
// Custom configuration in application.conf or test configuration
115
val customConfig = ConfigFactory.parseString("""
116
akka.test {
117
timefactor = 2.0
118
single-expect-default = 5s
119
multi-expect-default = 10s
120
expect-no-message-default = 200ms
121
filter-leeway = 5s
122
default-timeout = 30s
123
}
124
""")
125
126
val system = ActorSystem("CustomTestSystem", customConfig)
127
128
class CustomConfigTest extends TestKit(system) {
129
"Custom configuration" should {
130
"use overridden values" in {
131
val settings = TestKitExtension(system)
132
133
settings.TestTimeFactor should equal(2.0)
134
settings.SingleExpectDefaultTimeout should equal(5.seconds)
135
settings.MultiExpectDefaultTimeout should equal(10.seconds)
136
settings.ExpectNoMessageDefaultTimeout should equal(200.millis)
137
settings.TestEventFilterLeeway should equal(5.seconds)
138
settings.DefaultTimeout.duration should equal(30.seconds)
139
}
140
}
141
}
142
```
143
144
**Environment-Specific Configuration:**
145
146
```scala
147
// Different configurations for different environments
148
object TestConfigurations {
149
val ciConfig = ConfigFactory.parseString("""
150
akka.test {
151
timefactor = 3.0 # Slower CI environment
152
single-expect-default = 10s
153
filter-leeway = 10s
154
}
155
""")
156
157
val localConfig = ConfigFactory.parseString("""
158
akka.test {
159
timefactor = 1.0 # Fast local development
160
single-expect-default = 3s
161
filter-leeway = 3s
162
}
163
""")
164
165
val loadTestConfig = ConfigFactory.parseString("""
166
akka.test {
167
timefactor = 5.0 # Very slow load testing environment
168
single-expect-default = 30s
169
multi-expect-default = 60s
170
filter-leeway = 15s
171
}
172
""")
173
174
def getConfig(environment: String): Config = environment match {
175
case "ci" => ciConfig
176
case "local" => localConfig
177
case "load" => loadTestConfig
178
case _ => ConfigFactory.empty()
179
}
180
}
181
182
// Usage in tests
183
class EnvironmentConfigTest {
184
val environment = sys.env.getOrElse("TEST_ENV", "local")
185
val config = TestConfigurations.getConfig(environment)
186
val system = ActorSystem("EnvTestSystem", config)
187
188
// Tests will use environment-appropriate timeouts
189
}
190
```
191
192
### Integration with TestKit Features
193
194
**Automatic Timeout Usage:**
195
196
```scala
197
class TimeoutIntegrationTest extends TestKit(ActorSystem("TestSystem")) with DefaultTimeout {
198
"TestKit with configuration" should {
199
"use configured timeouts automatically" in {
200
val actor = system.actorOf(Props(new Actor {
201
def receive = {
202
case msg =>
203
Thread.sleep(100) // Simulate work
204
sender() ! s"processed: $msg"
205
}
206
}))
207
208
// Uses SingleExpectDefaultTimeout from configuration
209
actor ! "test"
210
expectMsg("processed: test") // No explicit timeout needed
211
212
// Uses ExpectNoMessageDefaultTimeout from configuration
213
expectNoMessage() // Uses configured default
214
}
215
216
"respect time dilation" in {
217
import akka.testkit._
218
219
// All dilated timeouts use TestTimeFactor
220
within(2.seconds.dilated) {
221
// Code that might run slowly in some environments
222
Thread.sleep(100)
223
expectNoMessage(100.millis.dilated)
224
}
225
}
226
}
227
}
228
```
229
230
**Event Filter Configuration Integration:**
231
232
```scala
233
class EventFilterConfigTest extends TestKit(ActorSystem("TestSystem")) {
234
"Event filters with configuration" should {
235
"use configured filter leeway" in {
236
val settings = TestKitExtension(system)
237
238
// EventFilter internally uses TestEventFilterLeeway for timeout
239
EventFilter.info(message = "test message").intercept {
240
// Simulate slow logging
241
Future {
242
Thread.sleep(settings.TestEventFilterLeeway.toMillis / 2)
243
log.info("test message")
244
}
245
246
// Filter waits up to filter-leeway time for the event
247
}
248
}
249
}
250
}
251
```
252
253
### Advanced Configuration Patterns
254
255
**Dynamic Configuration:**
256
257
```scala
258
class DynamicConfigTest {
259
def createSystemWithTimeoutFactor(factor: Double): ActorSystem = {
260
val config = ConfigFactory.parseString(s"""
261
akka.test.timefactor = $factor
262
""")
263
ActorSystem("DynamicTestSystem", config)
264
}
265
266
"Dynamic configuration" should {
267
"allow runtime configuration changes" in {
268
val fastSystem = createSystemWithTimeoutFactor(1.0)
269
val slowSystem = createSystemWithTimeoutFactor(5.0)
270
271
val fastKit = new TestKit(fastSystem)
272
val slowKit = new TestKit(slowSystem)
273
274
import fastKit._
275
val fastSettings = TestKitExtension(fastSystem)
276
fastSettings.TestTimeFactor should equal(1.0)
277
278
import slowKit._
279
val slowSettings = TestKitExtension(slowSystem)
280
slowSettings.TestTimeFactor should equal(5.0)
281
282
fastSystem.terminate()
283
slowSystem.terminate()
284
}
285
}
286
}
287
```
288
289
**Configuration Validation:**
290
291
```scala
292
class ConfigValidationTest extends TestKit(ActorSystem("TestSystem")) {
293
"Configuration validation" should {
294
"ensure reasonable timeout values" in {
295
val settings = TestKitExtension(system)
296
297
// Validate timeout values are reasonable
298
settings.SingleExpectDefaultTimeout should be >= 100.millis
299
settings.MultiExpectDefaultTimeout should be >= settings.SingleExpectDefaultTimeout
300
settings.TestEventFilterLeeway should be >= 1.second
301
settings.TestTimeFactor should be >= 1.0
302
303
// Validate dilated timeouts don't exceed reasonable bounds
304
val dilated = settings.dilated(1.minute)
305
dilated should be <= 10.minutes // Reasonable upper bound
306
}
307
308
"provide consistent configuration access" in {
309
// Multiple ways to access should return same instance
310
val settings1 = TestKitExtension(system)
311
val settings2 = TestKitSettings(system)
312
313
settings1 should be theSameInstanceAs settings2
314
settings1.TestTimeFactor should equal(settings2.TestTimeFactor)
315
}
316
}
317
}
318
```
319
320
### Best Practices
321
322
1. **Environment-Specific Configuration**: Use different time factors for different environments
323
2. **Reasonable Defaults**: Start with default configuration and adjust based on test performance
324
3. **Consistent Access**: Use TestKitExtension for consistent settings access
325
4. **Time Dilation**: Always use dilated timeouts for environment independence
326
5. **Configuration Testing**: Validate your configuration settings in tests
327
328
```scala
329
// Good: Environment-aware configuration
330
val timeFactor = if (sys.env.contains("CI")) 3.0 else 1.0
331
val config = ConfigFactory.parseString(s"akka.test.timefactor = $timeFactor")
332
333
// Good: Consistent settings access
334
val settings = TestKitExtension(system)
335
val timeout = settings.SingleExpectDefaultTimeout
336
337
// Good: Use dilated timeouts everywhere
338
expectMsg(5.seconds.dilated, expectedMessage)
339
within(10.seconds.dilated) { /* test code */ }
340
341
// Good: Validate configuration in tests
342
assert(settings.TestTimeFactor >= 1.0, "Time factor should be at least 1.0")
343
assert(settings.DefaultTimeout.duration > Duration.Zero, "Default timeout must be positive")
344
```
345
346
### Common Configuration Issues
347
348
**Performance Issues:**
349
```scala
350
// Problem: Time factor too high for local development
351
akka.test.timefactor = 10.0 // Makes tests unnecessarily slow locally
352
353
// Solution: Environment-specific factors
354
akka.test.timefactor = ${?TEST_TIME_FACTOR} // Override via environment variable
355
```
356
357
**Timeout Issues:**
358
```scala
359
// Problem: Timeouts too short for slow operations
360
akka.test.single-expect-default = 100ms // Too short for most operations
361
362
// Solution: Reasonable defaults with dilation
363
akka.test.single-expect-default = 3s
364
// Then use .dilated in tests for environment adaptation
365
```
366
367
**Filter Issues:**
368
```scala
369
// Problem: Filter leeway too short for async logging
370
akka.test.filter-leeway = 100ms // May miss async log events
371
372
// Solution: Adequate leeway for async operations
373
akka.test.filter-leeway = 3s
374
```