or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

actor-references.mdcore-testing.mddeterministic-execution.mdevent-filtering.mdindex.mdjava-api.mdtest-utilities.mdutilities-config.md
tile.json

java-api.mddocs/

Java API

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.

Modern Java API (javadsl)

javadsl.TestKit { .api }

class TestKit(system: ActorSystem) {
  // Java Duration-based methods
  def expectMsg(duration: java.time.Duration, obj: AnyRef): AnyRef
  def expectMsgClass(duration: java.time.Duration, clazz: Class[_]): AnyRef
  def expectMsgEquals(duration: java.time.Duration, obj: AnyRef): AnyRef
  def expectNoMessage(duration: java.time.Duration): Unit
  def expectNoMessage(): Unit
  
  // Supplier-based methods for lazy evaluation
  def within(duration: java.time.Duration, supplier: Supplier[_]): AnyRef
  def awaitCond(duration: java.time.Duration, supplier: Supplier[Boolean]): Unit
  def awaitAssert(duration: java.time.Duration, supplier: Supplier[_]): AnyRef
  
  // Message reception
  def receiveOne(duration: java.time.Duration): AnyRef
  def receiveN(n: Int, duration: java.time.Duration): java.util.List[AnyRef]
  
  // Actor references and system access
  def getSystem(): ActorSystem
  def getTestActor(): ActorRef
  def getLastSender(): ActorRef
  
  // Lifecycle
  def watch(actorRef: ActorRef): ActorRef
  def unwatch(actorRef: ActorRef): ActorRef
}

Modern Java API with java.time.Duration support and Java 8+ functional interfaces.

javadsl.EventFilter { .api }

class EventFilter(clazz: Class[_], system: ActorSystem) {
  def message(pattern: String): EventFilter
  def source(source: String): EventFilter  
  def occurrences(count: Int): EventFilter
  def from(source: String): EventFilter
  def intercept[T](supplier: Supplier[T]): T
  def matches(logEvent: LogEvent): Boolean
}

object EventFilter {
  def error(system: ActorSystem): EventFilter
  def warning(system: ActorSystem): EventFilter
  def info(system: ActorSystem): EventFilter
  def debug(system: ActorSystem): EventFilter
  def forException(clazz: Class[_ <: Throwable], system: ActorSystem): EventFilter
}

Fluent Java API for event filtering with method chaining.

Usage Examples

Basic TestKit Usage

import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.testkit.javadsl.TestKit;
import java.time.Duration;
import org.junit.Test;
import org.junit.AfterClass;

public class MyActorTest {
    static ActorSystem system = ActorSystem.create("TestSystem");
    
    @Test
    public void testActorResponse() {
        new TestKit(system) {{
            ActorRef actor = system.actorOf(Props.create(MyActor.class));
            
            // Send message and expect response
            actor.tell("ping", getTestActor());
            expectMsg(Duration.ofSeconds(1), "pong");
        }};
    }
    
    @Test
    public void testNoMessage() {
        new TestKit(system) {{
            ActorRef actor = system.actorOf(Props.create(QuietActor.class));
            
            actor.tell("ignore-me", getTestActor());
            expectNoMessage(Duration.ofMillis(500));
        }};
    }
    
    @AfterClass
    public static void teardown() {
        TestKit.shutdownActorSystem(system);
    }
}

Message Expectations with Types

import akka.testkit.javadsl.TestKit;

public class TypedMessageTest {
    @Test
    public void testTypedMessages() {
        new TestKit(system) {{
            ActorRef actor = system.actorOf(Props.create(StatusActor.class));
            
            // Expect specific message class
            actor.tell("get-status", getTestActor());
            StatusResponse response = expectMsgClass(
                Duration.ofSeconds(1), 
                StatusResponse.class
            );
            
            assertEquals("active", response.getStatus());
            assertEquals(42, response.getCount());
        }};
    }
}

Timing Control with Suppliers

import java.util.function.Supplier;

public class TimingTest {
    @Test
    public void testWithinBlock() {
        new TestKit(system) {{
            ActorRef actor = system.actorOf(Props.create(TimedActor.class));
            
            // Execute within time bounds using Supplier
            String result = (String) within(Duration.ofSeconds(3), () -> {
                actor.tell("timed-request", getTestActor());
                return expectMsg(Duration.ofSeconds(2), "timed-response");
            });
            
            assertEquals("timed-response", result);
        }};
    }
    
    @Test
    public void testAwaitCondition() {
        new TestKit(system) {{
            SharedState state = new SharedState();
            ActorRef actor = system.actorOf(Props.create(StateActor.class, state));
            
            actor.tell("initialize", getTestActor());
            
            // Wait for condition with Supplier
            awaitCond(Duration.ofSeconds(5), () -> state.isReady());
            
            assertTrue(state.isReady());
        }};
    }
}

Message Reception

public class MessageReceptionTest {
    @Test
    public void testReceiveMessages() {
        new TestKit(system) {{
            ActorRef actor = system.actorOf(Props.create(BatchActor.class));
            
            // Trigger batch of messages
            actor.tell("send-batch", getTestActor());
            
            // Receive single message
            Object first = receiveOne(Duration.ofSeconds(1));
            assertEquals("message-1", first);
            
            // Receive multiple messages
            List<Object> messages = receiveN(3, Duration.ofSeconds(2));
            assertEquals(3, messages.size());
            assertEquals("message-2", messages.get(0));
            assertEquals("message-3", messages.get(1));
            assertEquals("message-4", messages.get(2));
        }};
    }
}

Actor Lifecycle Testing

public class LifecycleTest {
    @Test 
    public void testActorTermination() {
        new TestKit(system) {{
            ActorRef actor = system.actorOf(Props.create(LifecycleActor.class));
            
            // Watch for termination
            watch(actor);
            
            // Trigger termination
            actor.tell(PoisonPill.getInstance(), ActorRef.noSender());
            
            // Expect termination message
            Terminated terminated = expectMsgClass(
                Duration.ofSeconds(1), 
                Terminated.class
            );
            
            assertEquals(actor, terminated.getActor());
        }};
    }
}

Event Filtering with Java API

Basic Event Filtering

import akka.testkit.javadsl.EventFilter;

public class EventFilterTest {
    @Test
    public void testErrorFiltering() {
        new TestKit(system) {{
            ActorRef actor = system.actorOf(Props.create(LoggingActor.class));
            
            // Filter expected error messages
            EventFilter.error(system)
                .message("Something went wrong")
                .occurrences(1)
                .intercept(() -> {
                    actor.tell("cause-error", getTestActor());
                    return expectMsg(Duration.ofSeconds(1), "error-handled");
                });
        }};
    }
    
    @Test
    public void testExceptionFiltering() {
        new TestKit(system) {{
            ActorRef actor = system.actorOf(Props.create(FaultyActor.class));
            
            // Filter specific exception type
            EventFilter.forException(RuntimeException.class, system)
                .occurrences(1)
                .intercept(() -> {
                    actor.tell("throw-exception", getTestActor());
                    return null;
                });
        }};
    }
}

Advanced Event Filtering

public class AdvancedEventFilterTest {
    @Test
    public void testChainedFiltering() {
        new TestKit(system) {{
            ActorRef actor = system.actorOf(Props.create(VerboseActor.class));
            
            // Chain multiple filter conditions
            EventFilter.info(system)
                .source("akka://TestSystem/user/verbose-actor")
                .message("Processing started")
                .occurrences(1)
                .intercept(() -> {
                    actor.tell("start-processing", getTestActor());
                    return expectMsg(Duration.ofSeconds(1), "processing-started");
                });
        }};
    }
    
    @Test
    public void testMultipleFilters() {
        new TestKit(system) {{
            ActorRef actor = system.actorOf(Props.create(MultiLogActor.class));
            
            // Multiple filters for different log levels
            EventFilter.warning(system).occurrences(1).intercept(() -> {
                return EventFilter.error(system).occurrences(1).intercept(() -> {
                    actor.tell("log-multiple", getTestActor());
                    return expectMsg(Duration.ofSeconds(1), "logged");
                });
            });
        }};
    }
}

TestProbe Java Usage

import akka.testkit.TestProbe;

public class TestProbeExample {
    @Test
    public void testWithProbe() {
        new TestKit(system) {{
            // Create test probe
            TestProbe probe = TestProbe.create(system);
            ActorRef actor = system.actorOf(Props.create(InteractionActor.class));
            
            // Register probe as listener
            actor.tell(new RegisterListener(probe.ref()), getTestActor());
            expectMsg(Duration.ofSeconds(1), "listener-registered");
            
            // Trigger notification
            actor.tell("notify-listeners", getTestActor());
            
            // Probe receives notification
            probe.expectMsg(Duration.ofSeconds(1), "notification");
            
            // Probe can reply
            probe.reply("acknowledged");
            
            expectMsg(Duration.ofSeconds(1), "ack-received");
        }};
    }
}

Legacy Java API (Deprecated)

JavaTestKit (Deprecated) { .api }

public class JavaTestKit {
    public JavaTestKit(ActorSystem system) { }
    
    // Legacy methods using Scala Duration
    public Object expectMsg(scala.concurrent.duration.Duration duration, Object obj)
    public Object expectMsgClass(scala.concurrent.duration.Duration duration, Class<?> clazz)
    public void expectNoMsg(scala.concurrent.duration.Duration duration)
    
    // Static utility methods
    public static void shutdownActorSystem(ActorSystem system)
    public static void shutdownActorSystem(ActorSystem system, scala.concurrent.duration.Duration duration)
    public static void shutdownActorSystem(ActorSystem system, scala.concurrent.duration.Duration duration, boolean verifySystemShutdown)
}

Note: JavaTestKit is deprecated in favor of the modern javadsl.TestKit.

Migration from Legacy API

// Old way (deprecated)
import akka.testkit.JavaTestKit;
import scala.concurrent.duration.Duration;

public class OldStyleTest extends JavaTestKit {
    public OldStyleTest() {
        super(system);
    }
    
    @Test
    public void oldTest() {
        ActorRef actor = system.actorOf(Props.create(MyActor.class));
        actor.tell("message", getRef());
        expectMsg(Duration.create(1, "second"), "response");
    }
}

// New way (recommended)
import akka.testkit.javadsl.TestKit;
import java.time.Duration;

public class NewStyleTest {
    @Test
    public void newTest() {
        new TestKit(system) {{
            ActorRef actor = system.actorOf(Props.create(MyActor.class));
            actor.tell("message", getTestActor());
            expectMsg(Duration.ofSeconds(1), "response");
        }};
    }
}

Java 8+ Features Integration

CompletableFuture Integration

import java.util.concurrent.CompletableFuture;

public class AsyncTest {
    @Test
    public void testAsyncOperations() {
        new TestKit(system) {{
            ActorRef actor = system.actorOf(Props.create(AsyncActor.class));
            
            // Use CompletableFuture with TestKit
            CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
                actor.tell("async-request", getTestActor());
                return (String) expectMsg(Duration.ofSeconds(5), "async-response");
            });
            
            String result = future.join();
            assertEquals("async-response", result);
        }};
    }
}

Stream Integration

import java.util.stream.IntStream;

public class StreamTest {
    @Test
    public void testWithStreams() {
        new TestKit(system) {{
            ActorRef actor = system.actorOf(Props.create(CounterActor.class));
            
            // Send multiple messages using streams
            IntStream.range(1, 6)
                .forEach(i -> actor.tell("increment", getTestActor()));
            
            // Expect multiple responses
            IntStream.range(1, 6)
                .forEach(expected -> {
                    Integer count = (Integer) expectMsgClass(Duration.ofSeconds(1), Integer.class);
                    assertEquals(expected, count.intValue());
                });
        }};
    }
}

Lambda Expression Support

public class LambdaTest {
    @Test
    public void testWithLambdas() {
        new TestKit(system) {{
            ActorRef actor = system.actorOf(Props.create(ProcessorActor.class));
            
            // Use lambda for within block
            String result = (String) within(Duration.ofSeconds(3), () -> {
                actor.tell("process", getTestActor());
                return expectMsg(Duration.ofSeconds(2), "processed");
            });
            
            // Use lambda for condition waiting
            awaitCond(Duration.ofSeconds(5), () -> {
                actor.tell("is-ready", getTestActor());
                Object response = receiveOne(Duration.ofMillis(100));
                return "ready".equals(response);
            });
        }};
    }
}

Best Practices for Java API

Test Structure

  1. Use TestKit pattern: Wrap test logic in TestKit anonymous class
  2. Proper cleanup: Always shutdown actor system after tests
  3. Use Java 8+ features: Leverage Duration, Supplier, and lambda expressions
  4. Clear assertions: Use descriptive assertion messages
// Good: Proper test structure
public class WellStructuredTest {
    private static ActorSystem system = ActorSystem.create("TestSystem");
    
    @Test
    public void testFeature() {
        new TestKit(system) {{
            // Test logic here
            ActorRef actor = system.actorOf(Props.create(MyActor.class));
            actor.tell("test", getTestActor());
            
            String response = (String) expectMsg(Duration.ofSeconds(1), "expected");
            assertEquals("Should receive correct response", "expected", response);
        }};
    }
    
    @AfterClass
    public static void cleanup() {
        TestKit.shutdownActorSystem(system);
    }
}

Error Handling

  1. Use try-catch blocks: Handle test exceptions appropriately
  2. Provide context: Include meaningful error messages
  3. Test negative cases: Verify error conditions
// Good: Comprehensive error handling
@Test
public void testErrorConditions() {
    new TestKit(system) {{
        ActorRef actor = system.actorOf(Props.create(FaultyActor.class));
        
        try {
            EventFilter.forException(IllegalStateException.class, system)
                .occurrences(1)
                .intercept(() -> {
                    actor.tell("invalid-state", getTestActor());
                    return null;
                });
        } catch (Exception e) {
            fail("Expected IllegalStateException was not thrown: " + e.getMessage());
        }
        
        // Verify actor recovers
        actor.tell("valid-request", getTestActor());
        expectMsg(Duration.ofSeconds(1), "recovered");
    }};
}

Type Safety

  1. Use typed expectations: Prefer expectMsgClass() over expectMsg() when possible
  2. Cast carefully: Use safe casting with instanceof checks
  3. Generic types: Leverage generics where available
// Good: Type-safe testing
@Test
public void testTypedMessages() {
    new TestKit(system) {{
        ActorRef actor = system.actorOf(Props.create(TypedActor.class));
        
        actor.tell("get-status", getTestActor());
        StatusMessage status = expectMsgClass(Duration.ofSeconds(1), StatusMessage.class);
        
        assertNotNull("Status should not be null", status);
        assertTrue("Status should be active", status.isActive());
        assertEquals("Count should match", 42, status.getCount());
    }};
}