0
# Helper Utilities
1
2
Additional utility functions and classes for common testing scenarios, including socket utilities, test exceptions, and serialization helpers.
3
4
## Capabilities
5
6
### SocketUtil Object
7
8
Utilities for getting free socket addresses in tests.
9
10
```scala { .api }
11
object SocketUtil {
12
// Port utilities
13
def temporaryLocalPort(udp: Boolean = false): Int
14
def temporaryLocalAddress(udp: Boolean = false): InetSocketAddress
15
16
// Server address utilities
17
def temporaryServerAddress(address: String = "127.0.0.1", udp: Boolean = false): InetSocketAddress
18
def temporaryServerAddresses(
19
numberOfAddresses: Int,
20
hostname: String = "127.0.0.1",
21
udp: Boolean = false
22
): Seq[InetSocketAddress]
23
24
// Unbound address utility
25
def notBoundServerAddress(address: String = "127.0.0.1", udp: Boolean = false): InetSocketAddress
26
}
27
```
28
29
**Usage Examples:**
30
31
```scala
32
import akka.testkit.SocketUtil
33
34
class NetworkingTest extends TestKit(ActorSystem("TestSystem")) {
35
"SocketUtil" should {
36
"provide free TCP ports" in {
37
val port1 = SocketUtil.temporaryLocalPort()
38
val port2 = SocketUtil.temporaryLocalPort()
39
40
port1 should not equal port2
41
port1 should be > 1024
42
port2 should be > 1024
43
44
// Use ports for testing
45
val server = startTestServer(port1)
46
val client = connectToServer("localhost", port1)
47
48
// Test networking code
49
client.send("hello")
50
server.expectReceive("hello")
51
}
52
53
"provide free UDP ports" in {
54
val udpPort = SocketUtil.temporaryLocalPort(udp = true)
55
val udpAddress = SocketUtil.temporaryLocalAddress(udp = true)
56
57
udpAddress.getPort should equal(udpPort)
58
59
// Use for UDP testing
60
val udpSocket = new DatagramSocket(udpPort)
61
// Test UDP functionality
62
}
63
64
"provide server addresses" in {
65
val address = SocketUtil.temporaryServerAddress("0.0.0.0")
66
address.getHostString should be("0.0.0.0")
67
address.getPort should be > 1024
68
69
// Multiple addresses for cluster testing
70
val addresses = SocketUtil.temporaryServerAddresses(3, "127.0.0.1")
71
addresses should have size 3
72
addresses.map(_.getPort).distinct should have size 3
73
}
74
}
75
}
76
```
77
78
### TestException Case Class
79
80
Predefined exception for tests without stack trace.
81
82
```scala { .api }
83
final case class TestException(message: String) extends RuntimeException(message) with NoStackTrace
84
```
85
86
**Usage Examples:**
87
88
```scala
89
import akka.testkit.TestException
90
91
class ExceptionTest extends TestKit(ActorSystem("TestSystem")) {
92
"TestException" should {
93
"be lightweight for testing" in {
94
val exception = TestException("Test error message")
95
96
exception.getMessage should be("Test error message")
97
exception.getStackTrace should be(empty) // No stack trace for performance
98
99
// Use in actor testing
100
EventFilter[TestException](message = "Test error message").intercept {
101
val actor = system.actorOf(Props(new Actor {
102
def receive = {
103
case "throw" => throw TestException("Test error message")
104
}
105
}))
106
actor ! "throw"
107
}
108
}
109
110
"be useful for expected failures" in {
111
class FailingActor extends Actor {
112
def receive = {
113
case "fail" => throw TestException("Expected failure")
114
case msg => sender() ! msg
115
}
116
}
117
118
val actor = system.actorOf(Props[FailingActor]())
119
120
// Test normal operation
121
actor ! "hello"
122
expectMsg("hello")
123
124
// Test expected failure
125
EventFilter[TestException](message = "Expected failure").intercept {
126
actor ! "fail"
127
}
128
}
129
}
130
}
131
```
132
133
### Java Serialization Utilities
134
135
Utilities for Java serialization in tests.
136
137
```scala { .api }
138
// Marker trait for test messages using Java serialization
139
trait JavaSerializable
140
141
// Java serializer for ad-hoc test messages
142
class TestJavaSerializer extends Serializer {
143
def identifier: Int
144
def includeManifest: Boolean
145
def toBinary(o: AnyRef): Array[Byte]
146
def fromBinary(bytes: Array[Byte], manifest: Option[Class[_]]): AnyRef
147
}
148
```
149
150
**Usage Examples:**
151
152
```scala
153
import akka.testkit.JavaSerializable
154
155
// Test message with Java serialization
156
case class TestMessage(data: String, id: Int) extends JavaSerializable
157
158
class SerializationTest extends TestKit(ActorSystem("TestSystem")) {
159
"Java serialization" should {
160
"work with test messages" in {
161
val message = TestMessage("test data", 123)
162
163
val actor = system.actorOf(Props(new Actor {
164
def receive = {
165
case msg: TestMessage => sender() ! msg.copy(data = msg.data.toUpperCase)
166
}
167
}))
168
169
actor ! message
170
expectMsg(TestMessage("TEST DATA", 123))
171
}
172
}
173
}
174
```
175
176
### TestKitUtils Object
177
178
Utility methods for TestKit operations.
179
180
```scala { .api }
181
object TestKitUtils {
182
// Extract test name from call stack
183
def testNameFromCallStack(classToStartFrom: Class[_], fallback: String): String
184
}
185
```
186
187
**Usage Example:**
188
189
```scala
190
import akka.testkit.TestKitUtils
191
192
class UtilsTest extends TestKit(ActorSystem("TestSystem")) {
193
"TestKitUtils" should {
194
"extract test names" in {
195
val testName = TestKitUtils.testNameFromCallStack(classOf[UtilsTest], "default-test")
196
testName should include("extract test names")
197
198
// Useful for dynamic actor naming
199
val actor = system.actorOf(Props[EchoActor](), s"echo-$testName")
200
actor.path.name should startWith("echo-")
201
}
202
}
203
}
204
```
205
206
### Configuration Utilities
207
208
Helper functions for test configuration management.
209
210
```scala { .api }
211
// Access test configuration
212
def testKitSettings(implicit system: ActorSystem): TestKitSettings = TestKitExtension(system)
213
214
// Time dilation helpers (from package object)
215
implicit class TestDuration(duration: FiniteDuration) {
216
def dilated(implicit system: ActorSystem): FiniteDuration
217
}
218
```
219
220
**Usage Examples:**
221
222
```scala
223
import akka.testkit._
224
import scala.concurrent.duration._
225
226
class ConfigurationTest extends TestKit(ActorSystem("TestSystem")) {
227
"Configuration utilities" should {
228
"provide test settings" in {
229
val settings = testKitSettings
230
231
settings.TestTimeFactor should be >= 1.0
232
settings.SingleExpectDefaultTimeout should be > Duration.Zero
233
settings.DefaultTimeout.duration should be > Duration.Zero
234
}
235
236
"dilate time for tests" in {
237
val originalDuration = 1.second
238
val dilated = originalDuration.dilated
239
240
// Dilated duration accounts for slow test environments
241
dilated should be >= originalDuration
242
243
// Use dilated timeouts in tests
244
within(5.seconds.dilated) {
245
// Test code that might run slowly in CI environments
246
Thread.sleep(100)
247
expectNoMessage(100.millis.dilated)
248
}
249
}
250
}
251
}
252
```
253
254
### Custom Test Utilities
255
256
You can create custom utilities for your specific testing needs:
257
258
```scala
259
// Custom test utilities object
260
object MyTestUtils {
261
// Helper for creating actors with test-specific configuration
262
def testActorOf[T <: Actor](props: Props, name: String)(implicit system: ActorSystem): ActorRef = {
263
system.actorOf(props.withDispatcher("akka.test.calling-thread-dispatcher"), name)
264
}
265
266
// Helper for temporary files in tests
267
def withTempFile[T](content: String)(test: File => T): T = {
268
val file = File.createTempFile("test", ".tmp")
269
try {
270
Files.write(file.toPath, content.getBytes)
271
test(file)
272
} finally {
273
file.delete()
274
}
275
}
276
277
// Helper for timing operations
278
def timed[T](operation: => T): (T, FiniteDuration) = {
279
val start = System.nanoTime()
280
val result = operation
281
val duration = (System.nanoTime() - start).nanos
282
(result, duration)
283
}
284
}
285
286
// Usage in tests
287
class CustomUtilsTest extends TestKit(ActorSystem("TestSystem")) {
288
import MyTestUtils._
289
290
"Custom utilities" should {
291
"help with actor creation" in {
292
val actor = testActorOf(Props[EchoActor](), "test-echo")
293
actor ! "hello"
294
expectMsg("hello")
295
}
296
297
"provide temp file support" in {
298
withTempFile("test content") { file =>
299
file.exists() should be(true)
300
Source.fromFile(file).mkString should be("test content")
301
}
302
}
303
304
"measure timing" in {
305
val (result, duration) = timed {
306
Thread.sleep(100)
307
"completed"
308
}
309
310
result should be("completed")
311
duration should be >= 100.millis
312
}
313
}
314
}
315
```
316
317
### Best Practices
318
319
1. **Use SocketUtil for Network Testing**: Always use free ports to avoid conflicts
320
2. **TestException for Expected Failures**: Use TestException for lightweight exception testing
321
3. **Time Dilation**: Use dilated timeouts for reliable CI testing
322
4. **Resource Cleanup**: Clean up sockets, files, and other resources after tests
323
5. **Descriptive Names**: Use meaningful names when creating temporary resources
324
325
```scala
326
// Good: Use utilities for reliable testing
327
val port = SocketUtil.temporaryLocalPort()
328
val server = startServer(port)
329
try {
330
// Test code
331
} finally {
332
server.stop()
333
}
334
335
// Good: Use time dilation for robustness
336
within(5.seconds.dilated) {
337
heavyOperation()
338
expectMsg("completed")
339
}
340
341
// Good: Use TestException for expected failures
342
EventFilter[TestException](message = "Expected test failure").intercept {
343
actor ! TriggerFailure()
344
}
345
```