0
# Java API
1
2
Akka TestKit provides a comprehensive Java API that offers the same functionality as the Scala API with Java-friendly method signatures, types, and modern Java 8+ features. The Java API is located in the `akka.testkit.javadsl` package and includes specialized classes designed for Java developers.
3
4
## Modern Java API (javadsl)
5
6
### javadsl.TestKit { .api }
7
8
```scala
9
class TestKit(system: ActorSystem) {
10
// Java Duration-based methods
11
def expectMsg(duration: java.time.Duration, obj: AnyRef): AnyRef
12
def expectMsgClass(duration: java.time.Duration, clazz: Class[_]): AnyRef
13
def expectMsgEquals(duration: java.time.Duration, obj: AnyRef): AnyRef
14
def expectNoMessage(duration: java.time.Duration): Unit
15
def expectNoMessage(): Unit
16
17
// Supplier-based methods for lazy evaluation
18
def within(duration: java.time.Duration, supplier: Supplier[_]): AnyRef
19
def awaitCond(duration: java.time.Duration, supplier: Supplier[Boolean]): Unit
20
def awaitAssert(duration: java.time.Duration, supplier: Supplier[_]): AnyRef
21
22
// Message reception
23
def receiveOne(duration: java.time.Duration): AnyRef
24
def receiveN(n: Int, duration: java.time.Duration): java.util.List[AnyRef]
25
26
// Actor references and system access
27
def getSystem(): ActorSystem
28
def getTestActor(): ActorRef
29
def getLastSender(): ActorRef
30
31
// Lifecycle
32
def watch(actorRef: ActorRef): ActorRef
33
def unwatch(actorRef: ActorRef): ActorRef
34
}
35
```
36
37
Modern Java API with `java.time.Duration` support and Java 8+ functional interfaces.
38
39
### javadsl.EventFilter { .api }
40
41
```scala
42
class EventFilter(clazz: Class[_], system: ActorSystem) {
43
def message(pattern: String): EventFilter
44
def source(source: String): EventFilter
45
def occurrences(count: Int): EventFilter
46
def from(source: String): EventFilter
47
def intercept[T](supplier: Supplier[T]): T
48
def matches(logEvent: LogEvent): Boolean
49
}
50
51
object EventFilter {
52
def error(system: ActorSystem): EventFilter
53
def warning(system: ActorSystem): EventFilter
54
def info(system: ActorSystem): EventFilter
55
def debug(system: ActorSystem): EventFilter
56
def forException(clazz: Class[_ <: Throwable], system: ActorSystem): EventFilter
57
}
58
```
59
60
Fluent Java API for event filtering with method chaining.
61
62
## Usage Examples
63
64
### Basic TestKit Usage
65
66
```java
67
import akka.actor.ActorRef;
68
import akka.actor.ActorSystem;
69
import akka.actor.Props;
70
import akka.testkit.javadsl.TestKit;
71
import java.time.Duration;
72
import org.junit.Test;
73
import org.junit.AfterClass;
74
75
public class MyActorTest {
76
static ActorSystem system = ActorSystem.create("TestSystem");
77
78
@Test
79
public void testActorResponse() {
80
new TestKit(system) {{
81
ActorRef actor = system.actorOf(Props.create(MyActor.class));
82
83
// Send message and expect response
84
actor.tell("ping", getTestActor());
85
expectMsg(Duration.ofSeconds(1), "pong");
86
}};
87
}
88
89
@Test
90
public void testNoMessage() {
91
new TestKit(system) {{
92
ActorRef actor = system.actorOf(Props.create(QuietActor.class));
93
94
actor.tell("ignore-me", getTestActor());
95
expectNoMessage(Duration.ofMillis(500));
96
}};
97
}
98
99
@AfterClass
100
public static void teardown() {
101
TestKit.shutdownActorSystem(system);
102
}
103
}
104
```
105
106
### Message Expectations with Types
107
108
```java
109
import akka.testkit.javadsl.TestKit;
110
111
public class TypedMessageTest {
112
@Test
113
public void testTypedMessages() {
114
new TestKit(system) {{
115
ActorRef actor = system.actorOf(Props.create(StatusActor.class));
116
117
// Expect specific message class
118
actor.tell("get-status", getTestActor());
119
StatusResponse response = expectMsgClass(
120
Duration.ofSeconds(1),
121
StatusResponse.class
122
);
123
124
assertEquals("active", response.getStatus());
125
assertEquals(42, response.getCount());
126
}};
127
}
128
}
129
```
130
131
### Timing Control with Suppliers
132
133
```java
134
import java.util.function.Supplier;
135
136
public class TimingTest {
137
@Test
138
public void testWithinBlock() {
139
new TestKit(system) {{
140
ActorRef actor = system.actorOf(Props.create(TimedActor.class));
141
142
// Execute within time bounds using Supplier
143
String result = (String) within(Duration.ofSeconds(3), () -> {
144
actor.tell("timed-request", getTestActor());
145
return expectMsg(Duration.ofSeconds(2), "timed-response");
146
});
147
148
assertEquals("timed-response", result);
149
}};
150
}
151
152
@Test
153
public void testAwaitCondition() {
154
new TestKit(system) {{
155
SharedState state = new SharedState();
156
ActorRef actor = system.actorOf(Props.create(StateActor.class, state));
157
158
actor.tell("initialize", getTestActor());
159
160
// Wait for condition with Supplier
161
awaitCond(Duration.ofSeconds(5), () -> state.isReady());
162
163
assertTrue(state.isReady());
164
}};
165
}
166
}
167
```
168
169
### Message Reception
170
171
```java
172
public class MessageReceptionTest {
173
@Test
174
public void testReceiveMessages() {
175
new TestKit(system) {{
176
ActorRef actor = system.actorOf(Props.create(BatchActor.class));
177
178
// Trigger batch of messages
179
actor.tell("send-batch", getTestActor());
180
181
// Receive single message
182
Object first = receiveOne(Duration.ofSeconds(1));
183
assertEquals("message-1", first);
184
185
// Receive multiple messages
186
List<Object> messages = receiveN(3, Duration.ofSeconds(2));
187
assertEquals(3, messages.size());
188
assertEquals("message-2", messages.get(0));
189
assertEquals("message-3", messages.get(1));
190
assertEquals("message-4", messages.get(2));
191
}};
192
}
193
}
194
```
195
196
### Actor Lifecycle Testing
197
198
```java
199
public class LifecycleTest {
200
@Test
201
public void testActorTermination() {
202
new TestKit(system) {{
203
ActorRef actor = system.actorOf(Props.create(LifecycleActor.class));
204
205
// Watch for termination
206
watch(actor);
207
208
// Trigger termination
209
actor.tell(PoisonPill.getInstance(), ActorRef.noSender());
210
211
// Expect termination message
212
Terminated terminated = expectMsgClass(
213
Duration.ofSeconds(1),
214
Terminated.class
215
);
216
217
assertEquals(actor, terminated.getActor());
218
}};
219
}
220
}
221
```
222
223
## Event Filtering with Java API
224
225
### Basic Event Filtering
226
227
```java
228
import akka.testkit.javadsl.EventFilter;
229
230
public class EventFilterTest {
231
@Test
232
public void testErrorFiltering() {
233
new TestKit(system) {{
234
ActorRef actor = system.actorOf(Props.create(LoggingActor.class));
235
236
// Filter expected error messages
237
EventFilter.error(system)
238
.message("Something went wrong")
239
.occurrences(1)
240
.intercept(() -> {
241
actor.tell("cause-error", getTestActor());
242
return expectMsg(Duration.ofSeconds(1), "error-handled");
243
});
244
}};
245
}
246
247
@Test
248
public void testExceptionFiltering() {
249
new TestKit(system) {{
250
ActorRef actor = system.actorOf(Props.create(FaultyActor.class));
251
252
// Filter specific exception type
253
EventFilter.forException(RuntimeException.class, system)
254
.occurrences(1)
255
.intercept(() -> {
256
actor.tell("throw-exception", getTestActor());
257
return null;
258
});
259
}};
260
}
261
}
262
```
263
264
### Advanced Event Filtering
265
266
```java
267
public class AdvancedEventFilterTest {
268
@Test
269
public void testChainedFiltering() {
270
new TestKit(system) {{
271
ActorRef actor = system.actorOf(Props.create(VerboseActor.class));
272
273
// Chain multiple filter conditions
274
EventFilter.info(system)
275
.source("akka://TestSystem/user/verbose-actor")
276
.message("Processing started")
277
.occurrences(1)
278
.intercept(() -> {
279
actor.tell("start-processing", getTestActor());
280
return expectMsg(Duration.ofSeconds(1), "processing-started");
281
});
282
}};
283
}
284
285
@Test
286
public void testMultipleFilters() {
287
new TestKit(system) {{
288
ActorRef actor = system.actorOf(Props.create(MultiLogActor.class));
289
290
// Multiple filters for different log levels
291
EventFilter.warning(system).occurrences(1).intercept(() -> {
292
return EventFilter.error(system).occurrences(1).intercept(() -> {
293
actor.tell("log-multiple", getTestActor());
294
return expectMsg(Duration.ofSeconds(1), "logged");
295
});
296
});
297
}};
298
}
299
}
300
```
301
302
## TestProbe Java Usage
303
304
```java
305
import akka.testkit.TestProbe;
306
307
public class TestProbeExample {
308
@Test
309
public void testWithProbe() {
310
new TestKit(system) {{
311
// Create test probe
312
TestProbe probe = TestProbe.create(system);
313
ActorRef actor = system.actorOf(Props.create(InteractionActor.class));
314
315
// Register probe as listener
316
actor.tell(new RegisterListener(probe.ref()), getTestActor());
317
expectMsg(Duration.ofSeconds(1), "listener-registered");
318
319
// Trigger notification
320
actor.tell("notify-listeners", getTestActor());
321
322
// Probe receives notification
323
probe.expectMsg(Duration.ofSeconds(1), "notification");
324
325
// Probe can reply
326
probe.reply("acknowledged");
327
328
expectMsg(Duration.ofSeconds(1), "ack-received");
329
}};
330
}
331
}
332
```
333
334
## Legacy Java API (Deprecated)
335
336
### JavaTestKit (Deprecated) { .api }
337
338
```java
339
public class JavaTestKit {
340
public JavaTestKit(ActorSystem system) { }
341
342
// Legacy methods using Scala Duration
343
public Object expectMsg(scala.concurrent.duration.Duration duration, Object obj)
344
public Object expectMsgClass(scala.concurrent.duration.Duration duration, Class<?> clazz)
345
public void expectNoMsg(scala.concurrent.duration.Duration duration)
346
347
// Static utility methods
348
public static void shutdownActorSystem(ActorSystem system)
349
public static void shutdownActorSystem(ActorSystem system, scala.concurrent.duration.Duration duration)
350
public static void shutdownActorSystem(ActorSystem system, scala.concurrent.duration.Duration duration, boolean verifySystemShutdown)
351
}
352
```
353
354
**Note**: JavaTestKit is deprecated in favor of the modern `javadsl.TestKit`.
355
356
### Migration from Legacy API
357
358
```java
359
// Old way (deprecated)
360
import akka.testkit.JavaTestKit;
361
import scala.concurrent.duration.Duration;
362
363
public class OldStyleTest extends JavaTestKit {
364
public OldStyleTest() {
365
super(system);
366
}
367
368
@Test
369
public void oldTest() {
370
ActorRef actor = system.actorOf(Props.create(MyActor.class));
371
actor.tell("message", getRef());
372
expectMsg(Duration.create(1, "second"), "response");
373
}
374
}
375
376
// New way (recommended)
377
import akka.testkit.javadsl.TestKit;
378
import java.time.Duration;
379
380
public class NewStyleTest {
381
@Test
382
public void newTest() {
383
new TestKit(system) {{
384
ActorRef actor = system.actorOf(Props.create(MyActor.class));
385
actor.tell("message", getTestActor());
386
expectMsg(Duration.ofSeconds(1), "response");
387
}};
388
}
389
}
390
```
391
392
## Java 8+ Features Integration
393
394
### CompletableFuture Integration
395
396
```java
397
import java.util.concurrent.CompletableFuture;
398
399
public class AsyncTest {
400
@Test
401
public void testAsyncOperations() {
402
new TestKit(system) {{
403
ActorRef actor = system.actorOf(Props.create(AsyncActor.class));
404
405
// Use CompletableFuture with TestKit
406
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
407
actor.tell("async-request", getTestActor());
408
return (String) expectMsg(Duration.ofSeconds(5), "async-response");
409
});
410
411
String result = future.join();
412
assertEquals("async-response", result);
413
}};
414
}
415
}
416
```
417
418
### Stream Integration
419
420
```java
421
import java.util.stream.IntStream;
422
423
public class StreamTest {
424
@Test
425
public void testWithStreams() {
426
new TestKit(system) {{
427
ActorRef actor = system.actorOf(Props.create(CounterActor.class));
428
429
// Send multiple messages using streams
430
IntStream.range(1, 6)
431
.forEach(i -> actor.tell("increment", getTestActor()));
432
433
// Expect multiple responses
434
IntStream.range(1, 6)
435
.forEach(expected -> {
436
Integer count = (Integer) expectMsgClass(Duration.ofSeconds(1), Integer.class);
437
assertEquals(expected, count.intValue());
438
});
439
}};
440
}
441
}
442
```
443
444
### Lambda Expression Support
445
446
```java
447
public class LambdaTest {
448
@Test
449
public void testWithLambdas() {
450
new TestKit(system) {{
451
ActorRef actor = system.actorOf(Props.create(ProcessorActor.class));
452
453
// Use lambda for within block
454
String result = (String) within(Duration.ofSeconds(3), () -> {
455
actor.tell("process", getTestActor());
456
return expectMsg(Duration.ofSeconds(2), "processed");
457
});
458
459
// Use lambda for condition waiting
460
awaitCond(Duration.ofSeconds(5), () -> {
461
actor.tell("is-ready", getTestActor());
462
Object response = receiveOne(Duration.ofMillis(100));
463
return "ready".equals(response);
464
});
465
}};
466
}
467
}
468
```
469
470
## Best Practices for Java API
471
472
### Test Structure
473
474
1. **Use TestKit pattern**: Wrap test logic in TestKit anonymous class
475
2. **Proper cleanup**: Always shutdown actor system after tests
476
3. **Use Java 8+ features**: Leverage Duration, Supplier, and lambda expressions
477
4. **Clear assertions**: Use descriptive assertion messages
478
479
```java
480
// Good: Proper test structure
481
public class WellStructuredTest {
482
private static ActorSystem system = ActorSystem.create("TestSystem");
483
484
@Test
485
public void testFeature() {
486
new TestKit(system) {{
487
// Test logic here
488
ActorRef actor = system.actorOf(Props.create(MyActor.class));
489
actor.tell("test", getTestActor());
490
491
String response = (String) expectMsg(Duration.ofSeconds(1), "expected");
492
assertEquals("Should receive correct response", "expected", response);
493
}};
494
}
495
496
@AfterClass
497
public static void cleanup() {
498
TestKit.shutdownActorSystem(system);
499
}
500
}
501
```
502
503
### Error Handling
504
505
1. **Use try-catch blocks**: Handle test exceptions appropriately
506
2. **Provide context**: Include meaningful error messages
507
3. **Test negative cases**: Verify error conditions
508
509
```java
510
// Good: Comprehensive error handling
511
@Test
512
public void testErrorConditions() {
513
new TestKit(system) {{
514
ActorRef actor = system.actorOf(Props.create(FaultyActor.class));
515
516
try {
517
EventFilter.forException(IllegalStateException.class, system)
518
.occurrences(1)
519
.intercept(() -> {
520
actor.tell("invalid-state", getTestActor());
521
return null;
522
});
523
} catch (Exception e) {
524
fail("Expected IllegalStateException was not thrown: " + e.getMessage());
525
}
526
527
// Verify actor recovers
528
actor.tell("valid-request", getTestActor());
529
expectMsg(Duration.ofSeconds(1), "recovered");
530
}};
531
}
532
```
533
534
### Type Safety
535
536
1. **Use typed expectations**: Prefer `expectMsgClass()` over `expectMsg()` when possible
537
2. **Cast carefully**: Use safe casting with instanceof checks
538
3. **Generic types**: Leverage generics where available
539
540
```java
541
// Good: Type-safe testing
542
@Test
543
public void testTypedMessages() {
544
new TestKit(system) {{
545
ActorRef actor = system.actorOf(Props.create(TypedActor.class));
546
547
actor.tell("get-status", getTestActor());
548
StatusMessage status = expectMsgClass(Duration.ofSeconds(1), StatusMessage.class);
549
550
assertNotNull("Status should not be null", status);
551
assertTrue("Status should be active", status.isActive());
552
assertEquals("Count should match", 42, status.getCount());
553
}};
554
}
555
```