How to test an EventHandler with EventSourcedBehaviorTestKit?

How to test an EventHaldler with EventSourcedBehaviorTestKit?

This topic is based on a conversation between an anonymous user of Akka and our “Ask AI about Akka” bot in the documentation.

Testing an EventSourcedBehavior with EventSourcedBehaviorTestKit involves creating an instance of EventSourcedBehaviorTestKit and running commands against it. The result of running a command contains the events emitted by the command and the new state after applying the events, which you can assert on.
Here is an example of how to do this in Scala:

class AccountExampleDocSpec
    extends ScalaTestWithActorTestKit(EventSourcedBehaviorTestKit.config)
    with AnyWordSpecLike
    with BeforeAndAfterEach
    with LogCapturing {

  private val eventSourcedTestKit =
    EventSourcedBehaviorTestKit[AccountEntity.Command, AccountEntity.Event, AccountEntity.Account](
      system,
      AccountEntity("1", PersistenceId("Account", "1")))

  override protected def beforeEach(): Unit = {
    super.beforeEach()
    eventSourcedTestKit.clear()
  }

  "Account" must {

    "be created with zero balance" in {
      val result = eventSourcedTestKit.runCommand[StatusReply[Done]](AccountEntity.CreateAccount(_))
      result.reply shouldBe StatusReply.Ack
      result.event shouldBe AccountEntity.AccountCreated
      result.stateOfType[AccountEntity.OpenedAccount].balance shouldBe 0
    }

    "handle Withdraw" in {
      eventSourcedTestKit.runCommand[StatusReply[Done]](AccountEntity.CreateAccount(_))
      // ... more tests
    }
  }
}

And here is an example in Java:

@ClassRule
public static final TestKitJunitResource testKit =
    new TestKitJunitResource(EventSourcedBehaviorTestKit.config());

private EventSourcedBehaviorTestKit<
        AccountEntity.Command, AccountEntity.Event, AccountEntity.Account>
    eventSourcedTestKit =
        EventSourcedBehaviorTestKit.create(
            testKit.system(), AccountEntity.create("1", PersistenceId.of("Account", "1")));

@Before
public void beforeEach() {
  eventSourcedTestKit.clear();
}

@Test
public void createWithEmptyBalance() {
  CommandResultWithReply<
          AccountEntity.Command, AccountEntity.Event, AccountEntity.Account, StatusReply<Done>>
      result = eventSourcedTestKit.runCommand(AccountEntity.CreateAccount::new);
  assertEquals(StatusReply.ack(), result.reply());
  assertEquals(AccountEntity.AccountCreated.INSTANCE, result.event());
  assertEquals(BigDecimal.ZERO, result.stateOfType(AccountEntity.OpenedAccount.class).balance);
}

In both examples, the EventSourcedBehaviorTestKit is created with the EventSourcedBehavior under test. The runCommand method is used to send commands to the behavior, and the result is used to assert on the emitted events and the new state.
For more details, you can refer to the Akka documentation on unit testing with the ActorTestKit and EventSourcedBehaviorTestKit.

How to test an EventHandler is executed after reading from journal in eventsourced actor?

Testing that an EventHandler is executed after reading from the journal in an EventSourcedBehavior involves using the EventSourcedBehaviorTestKit. This test kit allows you to run commands against your behavior and assert on the events and state changes that result from those commands.

Here’s an example of how you might do this in Scala:

val eventSourcedTestKit =
  EventSourcedBehaviorTestKit[Command, Event, State](
    system,
    MyPersistentBehavior(PersistenceId.ofUniqueId("abc")))

val result = eventSourcedTestKit.runCommand(MyCommand)
result.event shouldBe MyEvent
result.stateOfType[MyState].myProperty shouldBe expectedValue

In this example, MyPersistentBehavior is the EventSourcedBehavior you’re testing, MyCommand is a command that will trigger an event, MyEvent is the expected event, and MyState is the expected state after the event has been handled.

In Java, the code would look like this:

EventSourcedBehaviorTestKit<Command, Event, State> eventSourcedTestKit =
  EventSourcedBehaviorTestKit.create(
    system,
    MyPersistentBehavior.create(PersistenceId.ofUniqueId("abc")));

CommandResultWithReply<Command, Event, State, Reply> result =
  eventSourcedTestKit.runCommand(reply -> new MyCommand(reply));
assertEquals(MyEvent.class, result.event().getClass());
assertEquals(expectedValue, result.stateOfType(MyState.class).getMyProperty());

In both examples, the runCommand method is used to send a command to the behavior. The result of this method includes the events emitted by the command and the new state after the events have been applied. You can then assert that the correct event was emitted and that the state has been updated correctly.
For more details, you can refer to the Akka documentation on unit testing with the ActorTestKit and EventSourcedBehaviorTestKit.

How to manually send events to actor with EventSourcedBehaviorTestKit?

The EventSourcedBehaviorTestKit does not directly support manually sending events to an actor. It is designed to test the behavior of an actor in response to commands, not direct event inputs.

When you run a command using EventSourcedBehaviorTestKit.runCommand(), it will return a result that contains the events emitted by the command and the new state after applying the events.

How to test that EventSourcedBehavior actor replays events from journal?

Testing that an EventSourcedBehavior actor replays events from the journal involves using the EventSourcedBehaviorTestKit. This test kit allows you to run commands against your behavior and assert on the events and state changes that result from those commands.

To test event replay, you can use the restart method of the EventSourcedBehaviorTestKit. This will restart the behavior, which will then recover from stored snapshot and events from previous commands.

Here’s an example of how you might do this in Scala:

val eventSourcedTestKit =
  EventSourcedBehaviorTestKit[Command, Event, State](
    system,
    MyPersistentBehavior(PersistenceId.ofUniqueId("abc")))

// Run a command that results in an event
val result = eventSourcedTestKit.runCommand(MyCommand)
result.event shouldBe MyEvent

// Restart the behavior
eventSourcedTestKit.restart()

// Run another command and assert that the state has been updated correctly
val resultAfterRestart = eventSourcedTestKit.runCommand(AnotherCommand)
resultAfterRestart.stateOfType[MyState].myProperty shouldBe expectedValue

In Java, the code would look like this:

EventSourcedBehaviorTestKit<Command, Event, State> eventSourcedTestKit =
  EventSourcedBehaviorTestKit.create(
    system,
    MyPersistentBehavior.create(PersistenceId.ofUniqueId("abc")));

// Run a command that results in an event
CommandResultWithReply<Command, Event, State, Reply> result =
  eventSourcedTestKit.runCommand(reply -> new MyCommand(reply));
assertEquals(MyEvent.class, result.event().getClass());

// Restart the behavior
eventSourcedTestKit.restart();

// Run another command and assert that the state has been updated correctly
CommandResultWithReply<Command, Event, State, Reply> resultAfterRestart =
  eventSourcedTestKit.runCommand(reply -> new AnotherCommand(reply));
assertEquals(expectedValue, resultAfterRestart.stateOfType(MyState.class).getMyProperty());

In both examples, the runCommand method is used to send a command to the behavior. The restart method is then used to simulate a restart of the actor, which will cause it to replay events from the journal. You can then run another command and assert that the state has been updated correctly, which indicates that the events were replayed correctly.

In EventSourcedBehaviorTestKit how to create a probe to receive command responses?

In EventSourcedBehaviorTestKit, you don’t need to create a probe to receive command responses. The runCommand method returns a CommandResultWithReply object that contains the reply to the command, the events that were emitted, and the new state of the actor.

Here’s an example in Scala:

val eventSourcedTestKit =
  EventSourcedBehaviorTestKit[Command, Event, State](
    system,
    MyPersistentBehavior(PersistenceId.ofUniqueId("abc")))

val result = eventSourcedTestKit.runCommand(MyCommand)
val reply = result.reply // This is the reply to the command

And in Java:

EventSourcedBehaviorTestKit<Command, Event, State> eventSourcedTestKit =
  EventSourcedBehaviorTestKit.create(
    system,
    MyPersistentBehavior.create(PersistenceId.ofUniqueId("abc")));

CommandResultWithReply<Command, Event, State, Reply> result =
  eventSourcedTestKit.runCommand(reply -> new MyCommand(reply));
Reply reply = result.reply(); // This is the reply to the command

In both examples, the runCommand method is used to send a command to the behavior. The result of this method includes the reply to the command, the events emitted by the command, and the new state after the events have been applied.

What if the command includes an ActorRef to reply?

If the command includes an ActorRef for the reply, you can use a TestProbe to receive the reply. A TestProbe is a special kind of actor used for testing, which allows you to assert on the messages it has received.

Here’s an example in Scala:

val eventSourcedTestKit =
  EventSourcedBehaviorTestKit[Command, Event, State](
    system,
    MyPersistentBehavior(PersistenceId.ofUniqueId("abc")))

val probe = testKit.createTestProbe[Reply]()
val result = eventSourcedTestKit.runCommand(replyTo => MyCommand(replyTo))

probe.expectMessage(MyExpectedReply)

And in Java:

EventSourcedBehaviorTestKit<Command, Event, State> eventSourcedTestKit =
  EventSourcedBehaviorTestKit.create(
    system,
    MyPersistentBehavior.create(PersistenceId.ofUniqueId("abc")));

TestProbe<Reply> probe = testKit.createTestProbe();
eventSourcedTestKit.runCommand(replyTo -> new MyCommand(replyTo));

probe.expectMessage(MyExpectedReply);

In both examples, a TestProbe is created to receive the reply. The runCommand method is then used to send a command to the behavior, with the TestProbe’s ActorRef passed in for the reply. The expectMessage method is then used on the TestProbe to assert that it received the expected reply.

How to convert TestProbe to ActorRef?

In Akka, you can get the ActorRef of a TestProbe using the ref method (in Scala) or the getRef() method (in Java).

Here’s an example in Scala:

val probe = testKit.createTestProbe[Reply]()
val actorRef: ActorRef[Reply] = probe.ref

And in Java:

TestProbe<Reply> probe = testKit.createTestProbe();
ActorRef<Reply> actorRef = probe.getRef();

In both examples, a TestProbe is created and its ActorRef is obtained. This ActorRef can then be used to send messages to the TestProbe.

For more details, you can refer to the Akka documentation on testing.

How to get ActorContext from EventSourcedBehaviorTestKit?

The EventSourcedBehaviorTestKit does not provide a direct way to access the ActorContext. The ActorContext is typically used within the EventSourcedBehavior itself, not in the test code.

If you need to use the ActorContext in your EventSourcedBehavior, you can obtain it by wrapping the behavior construction with Behaviors.setup. Here’s an example in Scala:

def apply(): Behavior[String] =
  Behaviors.setup { context =>
    EventSourcedBehavior[String, String, State](
      persistenceId = PersistenceId.ofUniqueId("myPersistenceId"),
      emptyState = State(),
      commandHandler = CommandHandler.command { cmd =>
        context.log.info("Got command {}", cmd)
        Effect.none
      },
      eventHandler = {
        case (state, _) => state
      })
  }

And in Java:

public static Behavior<Command> create(PersistenceId persistenceId) {
    return Behaviors.setup(ctx -> new MyPersistentBehavior(persistenceId, ctx));
}

private final ActorContext<Command> context;

public MyPersistentBehavior(PersistenceId persistenceId, ActorContext<Command> ctx) {
    super(persistenceId);
    this.context = ctx;
}

In both examples, Behaviors.setup is used to create the EventSourcedBehavior, and the ActorContext is passed into the constructor of the behavior.

For more details, you can refer to the Akka documentation on accessing the ActorContext.

With EventSourcedBehaviorTestKit, can I test an actor with wrapped EventSourcedBehavior inside Behavior?

Yes, you can test an actor with a wrapped EventSourcedBehavior using EventSourcedBehaviorTestKit. The test kit is designed to work with EventSourcedBehavior, whether it’s used directly or wrapped inside another behavior.

Here’s an example in Scala:

val eventSourcedTestKit =
  EventSourcedBehaviorTestKit[Command, Event, State](
    system,
    Behaviors.setup { context =>
      MyPersistentBehavior(PersistenceId.ofUniqueId("abc"), context)
    })

val result = eventSourcedTestKit.runCommand(MyCommand)

And in Java:

EventSourcedBehaviorTestKit<Command, Event, State> eventSourcedTestKit =
  EventSourcedBehaviorTestKit.create(
    system,
    Behaviors.setup(ctx -> MyPersistentBehavior.create(PersistenceId.ofUniqueId("abc"), ctx)));

CommandResultWithReply<Command, Event, State, Reply> result =
  eventSourcedTestKit.runCommand(reply -> new MyCommand(reply));

In both examples, Behaviors.setup is used to create the EventSourcedBehavior, and the EventSourcedBehaviorTestKit is used to test it.

For more details, you can refer to the Akka documentation on unit testing with the ActorTestKit and EventSourcedBehaviorTestKit and the Akka documentation on wrapping EventSourcedBehavior.

This code leds to the error message “Only EventSourcedBehavior, or nested EventSourcedBehavior allowed.”

The error message “Only EventSourcedBehavior, or nested EventSourcedBehavior allowed” indicates that the EventSourcedBehaviorTestKit is being used with a behavior that is not an EventSourcedBehavior or a behavior that wraps an EventSourcedBehavior.

The EventSourcedBehaviorTestKit is specifically designed to test EventSourcedBehavior actors. If you’re trying to test a different kind of actor, you should use the ActorTestKit instead.

If you’re trying to test an EventSourcedBehavior that is wrapped inside another behavior, make sure that the wrapping behavior is properly set up to create the EventSourcedBehavior. Here’s an example of how to do this in Scala:

def apply(): Behavior[Command] =
  Behaviors.setup { context =>
    EventSourcedBehavior[Command, Event, State](
      persistenceId = PersistenceId.ofUniqueId("abc"),
      emptyState = State(),
      commandHandler = (state, cmd) => throw new NotImplementedError("TODO: process the command & return an Effect"),
      eventHandler = (state, evt) => throw new NotImplementedError("TODO: process the event return the next state"))
  }

And in Java:

public static Behavior<Command> create(PersistenceId persistenceId) {
  return Behaviors.setup(ctx -> new MyPersistentBehavior(persistenceId, ctx));
}

private MyPersistentBehavior(PersistenceId persistenceId, ActorContext<Command> ctx) {
  super(persistenceId);
  // setup your EventSourcedBehavior here
}

In both examples, Behaviors.setup is used to create the EventSourcedBehavior.