Complete Java API providing equivalent functionality to the Scala API with Java-friendly method signatures and type usage.
Java API for TestKit functionality with Java-friendly types and method signatures.
package akka.testkit.javadsl
class TestKit(system: ActorSystem) {
// Message expectation methods
def expectMsg(msg: Object): Object
def expectMsg(max: java.time.Duration, msg: Object): Object
def expectMsgClass[T](c: Class[T]): T
def expectMsgClass[T](max: java.time.Duration, c: Class[T]): T
def expectMsgAnyOf(objects: Object*): Object
def expectMsgAnyOf(max: java.time.Duration, objects: Object*): Object
def expectMsgAllOf(objects: Object*): java.util.List[Object]
def expectMsgAllOf(max: java.time.Duration, objects: Object*): java.util.List[Object]
def expectNoMessage(): Unit
def expectNoMessage(max: java.time.Duration): Unit
// Message reception methods
def receiveN(n: Int): java.util.List[Object]
def receiveN(n: Int, max: java.time.Duration): java.util.List[Object]
def receiveOne(max: java.time.Duration): Object
// Timing and condition methods
def within[T](max: java.time.Duration, f: Supplier[T]): T
def within[T](min: java.time.Duration, max: java.time.Duration, f: Supplier[T]): T
def awaitCond(p: Supplier[java.lang.Boolean]): Unit
def awaitCond(p: Supplier[java.lang.Boolean], max: java.time.Duration): Unit
def awaitCond(p: Supplier[java.lang.Boolean], max: java.time.Duration, interval: java.time.Duration): Unit
def awaitAssert[A](a: Supplier[A]): A
def awaitAssert[A](a: Supplier[A], max: java.time.Duration): A
def awaitAssert[A](a: Supplier[A], max: java.time.Duration, interval: java.time.Duration): A
// Actor management methods
def watch(ref: ActorRef): ActorRef
def unwatch(ref: ActorRef): ActorRef
def childActorOf(props: Props): ActorRef
def childActorOf(props: Props, name: String): ActorRef
// Properties
def getTestActor(): ActorRef
def getSystem(): ActorSystem
def duration(s: String): java.time.Duration
def dilated(d: java.time.Duration): java.time.Duration
}Usage Example:
import akka.actor.*;
import akka.testkit.javadsl.TestKit;
import org.junit.Test;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import java.time.Duration;
public class JavaTestKitExample {
static ActorSystem system;
@BeforeClass
public static void setup() {
system = ActorSystem.create("TestSystem");
}
@AfterClass
public static void teardown() {
TestKit.shutdownActorSystem(system);
system = null;
}
// Simple echo actor for testing
static class EchoActor extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.matchAny(msg -> getSender().tell(msg, getSelf()))
.build();
}
}
@Test
public void testEchoActor() {
new TestKit(system) {{
final ActorRef echoActor = system.actorOf(Props.create(EchoActor.class));
// Basic message expectation
echoActor.tell("hello", getTestActor());
expectMsg("hello");
// Expectation with timeout
echoActor.tell(42, getTestActor());
expectMsg(Duration.ofSeconds(3), 42);
// Expect specific type
echoActor.tell("test", getTestActor());
expectMsgClass(String.class);
// Multiple messages
echoActor.tell("first", getTestActor());
echoActor.tell("second", getTestActor());
expectMsgAllOf("first", "second");
}};
}
@Test
public void testTimingAndConditions() {
new TestKit(system) {{
final boolean[] condition = {false};
// Schedule condition to become true
system.scheduler().scheduleOnce(
Duration.ofMillis(500),
() -> condition[0] = true,
system.dispatcher()
);
// Wait for condition
awaitCond(() -> condition[0], Duration.ofSeconds(2));
// Time-bounded execution
within(Duration.ofSeconds(1), () -> {
expectNoMessage(Duration.ofMillis(100));
return null;
});
}};
}
}Java API for event filtering with builder-style methods.
package akka.testkit.javadsl
class EventFilter {
// Execution methods
def intercept[T](code: Supplier[T]): T
def intercept(code: Runnable): Unit
// Configuration methods (return new EventFilter for chaining)
def message(msg: String): EventFilter
def startsWith(msg: String): EventFilter
def matches(regex: String): EventFilter
def from(source: String): EventFilter
def occurrences(number: Int): EventFilter
// Static factory methods
static EventFilter error(): EventFilter
static EventFilter error(Class[_ <: Throwable] clazz): EventFilter
static EventFilter warning(): EventFilter
static EventFilter info(): EventFilter
static EventFilter debug(): EventFilter
static EventFilter custom(Function[LogEvent, java.lang.Boolean] test): EventFilter
}Usage Example:
import akka.testkit.javadsl.EventFilter;
import akka.event.Logging;
public class JavaEventFilterExample {
@Test
public void testEventFiltering() {
new TestKit(system) {{
// Simple error filtering
EventFilter.error()
.message("Expected error")
.occurrences(1)
.intercept(() -> {
ActorRef actor = system.actorOf(Props.create(ThrowingActor.class));
actor.tell("throw", getTestActor());
});
// Warning filtering with pattern
EventFilter.warning()
.matches("User \\d+ logged in")
.intercept(() -> {
// Code that logs user login messages
});
// Custom filtering
EventFilter.custom(event ->
event instanceof Logging.Error &&
event.message().toString().contains("database")
).intercept(() -> {
// Code that generates database errors
});
// Multiple filters (using separate calls)
EventFilter.error().occurrences(1).intercept(() -> {
EventFilter.warning().startsWith("Deprecated").intercept(() -> {
// Code that generates both errors and warnings
});
});
}};
}
}TestKit integrates well with Java 8+ functional interfaces:
import java.util.function.*;
import java.util.concurrent.CompletableFuture;
public class JavaFunctionalExample {
@Test
public void testWithLambdas() {
new TestKit(system) {{
// Using lambdas with awaitCond
CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> "result")
.thenApplyAsync(s -> s.toUpperCase());
awaitCond(() -> future.isDone(), Duration.ofSeconds(2));
// Using method references
Consumer<String> sendToActor = msg ->
getTestActor().tell(msg, ActorRef.noSender());
within(Duration.ofSeconds(1), () -> {
sendToActor.accept("test message");
expectMsg("test message");
return null;
});
// Functional event filtering
EventFilter.custom(event ->
event.logLevel().equals(Logging.InfoLevel()) &&
event.toString().contains("important")
).intercept(() -> {
// Code that generates important info messages
});
}};
}
}TestKit methods return Java collections where appropriate:
import java.util.*;
public class JavaCollectionsExample {
@Test
public void testCollections() {
new TestKit(system) {{
ActorRef actor = system.actorOf(Props.create(EchoActor.class));
// Send multiple messages
List<String> messages = Arrays.asList("msg1", "msg2", "msg3");
messages.forEach(msg -> actor.tell(msg, getTestActor()));
// Receive as Java List
List<Object> received = receiveN(3, Duration.ofSeconds(2));
// Convert and verify
List<String> receivedStrings = received.stream()
.map(Object::toString)
.collect(java.util.stream.Collectors.toList());
assertEquals(messages, receivedStrings);
// Expect multiple messages (returns Java List)
actor.tell("a", getTestActor());
actor.tell("b", getTestActor());
List<Object> results = expectMsgAllOf(Duration.ofSeconds(1), "a", "b");
assertEquals(2, results.size());
assertTrue(results.contains("a"));
assertTrue(results.contains("b"));
}};
}
}public class JavaExceptionHandlingExample {
@Test
public void testExceptionHandling() {
new TestKit(system) {{
// Handle timeout exceptions
try {
expectMsg(Duration.ofMillis(100), "never-sent");
fail("Should have timed out");
} catch (AssertionError e) {
// Expected timeout
assertTrue(e.getMessage().contains("timeout"));
}
// Test actor failures with event filtering
EventFilter.error(RuntimeException.class)
.message("Test exception")
.intercept(() -> {
ActorRef actor = system.actorOf(Props.create(FailingActor.class));
actor.tell("fail", getTestActor());
});
}};
}
static class FailingActor extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("fail", msg -> {
throw new RuntimeException("Test exception");
})
.build();
}
}
}JUnit Integration:
import org.junit.*;
import org.junit.rules.TestName;
public class JUnitIntegrationExample {
private static ActorSystem system;
private TestKit testKit;
@Rule
public TestName testName = new TestName();
@BeforeClass
public static void setupClass() {
system = ActorSystem.create("TestSystem");
}
@Before
public void setup() {
testKit = new TestKit(system);
}
@After
public void cleanup() {
// Individual test cleanup if needed
}
@AfterClass
public static void teardownClass() {
TestKit.shutdownActorSystem(system);
}
@Test
public void testActorBehavior() {
// Use testKit for testing
ActorRef actor = system.actorOf(Props.create(MyActor.class),
"actor-" + testName.getMethodName());
actor.tell("test", testKit.getTestActor());
testKit.expectMsg("response");
}
}TestNG Integration:
import org.testng.annotations.*;
public class TestNGIntegrationExample {
private ActorSystem system;
private TestKit testKit;
@BeforeClass
public void setupClass() {
system = ActorSystem.create("TestSystem");
}
@BeforeMethod
public void setup() {
testKit = new TestKit(system);
}
@AfterClass
public void teardownClass() {
TestKit.shutdownActorSystem(system);
}
@Test
public void testExample() {
// TestNG test using TestKit
testKit.within(Duration.ofSeconds(2), () -> {
// Test code
return null;
});
}
}// Good: Use lambdas and proper timeouts
testKit.awaitCond(() -> actor.isTerminated(), Duration.ofSeconds(3));
// Good: Type-safe message expectations
String response = testKit.expectMsgClass(Duration.ofSeconds(1), String.class);
// Good: Functional event filtering
EventFilter.custom(event -> event.logLevel().equals(Logging.ErrorLevel()))
.intercept(() -> triggerError());
// Good: Resource cleanup
@After
public void cleanup() {
if (testKit != null) {
// Clean up test resources
}
}