Testing exception thrown inside aggregate using aggregate fixture

If I have an event handler like:

    @EventSourcingHandler
    fun on(event: MonthEndedEvent) {
        if (currentBalance != event.endBalance) {
            throw EndBalanceDoesNotMatchCurrentBalanceException(
                ledgerId = event.ledgerId,
                currentBalance,
                event.endBalance,
            )
        }
    }

How can I test that it was thrown and has the correct message?

I tried something like:

fixture
    .given(monthStartedEvent)
    .`when`(
        EndMonthCommand(
            ledgerId = ledgerId,
            endBalance = mismatchedBalance,
        )
    )
    .expectException(MessageHandlerInvocationException::class.java) // Expect wrapper exception
    .expectExceptionDetails(
        Matchers.predicate<Exception> {
           it.cause is EndBalanceDoesNotMatchCurrentBalanceException
        }
    )
    .expectNoEvents()

But does not produce the desired result.

Hi Miguel,

This type of validation should occur in the command handler for the EndMonthCommand (event sourcing handlers should only set/update the aggregate state); try moving it there and throw an exception if validation fails.

1 Like

You are 100% correct. And I actually knew that… Brain fart I guess…

But I still have a doubt on how I validate the exception message.

 fixture
            .given(monthStartedEvent)
            .`when`(
                EndMonthCommand(
                    ledgerId = ledgerId,
                    endBalance = mismatchedBalance,
                )
            )
            .expectException(EndBalanceDoesNotMatchCurrentBalanceException::class.java)
            .expectExceptionDetails(
                Matchers.predicate<EndBalanceDoesNotMatchCurrentBalanceException> {
                    it.message == "Expected exception message"
                }
            )
            .expectNoEvents()

throws error:

Expected <[matches a given predicate]>,
 but got <details [null]>.

I assume it’s something very basic, but not clear on what.

Thank you.

Short answer - validate the message using expectExceptionMessage(String).

Slightly longer answer - “exception details” is something that is used when you have a distributed application scenario; when a command handler in one process receives a command from a client running in another process, any exceptions that are thrown must be of type CommandExecutionException, wherein you can provide your custom details to serialize & send back to the client.

Say you’re performing “balance” validation as in your case; in the non-distributed scenario you could simply throw the EndBalanceDoesNotMatchCurrentBalanceException; in the distributed scenario you’d have to do something like the following:

// in the command handler.
if (currentBalance != event.endBalance) {
 throw EndBalanceDoesNotMatchCurrentBalanceException(
    ledgerId = event.ledgerId,
    currentBalance,
    event.endBalance,
 )
}

// elsewhere in the aggregate class.
@ExceptionHandler
public void handle(EndBalanceDoesNotMatchCurrentBalanceException ex) {
 throw new CommandExecutionException(
  "An error occurred during command handling",
  ex,
  // these are the exception details to possibly validate.
  mapExceptionToDetails(ex)
 );
}

/Marc

That makes perfect sense.
Thank you so much for the explanation. This framework keeps amazing with its complexity. :slight_smile:

Keep up the good work.

1 Like