Saga test command handler send returns null

According to the CommandGateway sendAndWait, if null is returned that means the action timed out.

In my Saga I added a check for a null response that throws an exception, with the intent of forcing a retry of the event.

I now was trying to write a unit test for the saga but it seems the registered command gateway always returns null which triggers my check and throws the exception.

What is the proper way of doing this?

My command send function (used in Sagas):

fun <T : Any> sendCommandAndWait(command: T, eventName: String) {
        try {
            val result = commandGateway.sendAndWait<T>(command)

            if (result === null) {
                throw SagaEventHandlingTimedOut(this.javaClass.simpleName, eventName)
            }
        } catch (exception: SagaEventHandlingTimedOut) {
            log.warn("${exception.javaClass.simpleName}: ${exception.message}")

            throw exception
        } catch (exception: Throwable) {
            var cause: Throwable? = exception

            do {
                log.error("${cause?.javaClass?.simpleName}: ${cause?.message}")

                cause = exception.cause
            } while (cause != null)

            throw exception
        }
    }

My test:

@ExtendWith(MockKExtension::class)
class LedgerLifecycleSagaTest {
    private lateinit var fixture: FixtureConfiguration
    private val accountId = AccountId.fromString("c5be2bf8-4ffa-4b3e-a152-518cec206b1d")
    private val name = "Bcp mumia"
    private val balance = Money(BigDecimal.valueOf(13.00), EUR)
    private val date = Date.fromString("2014-01-02")
    private val ledgerId = LedgerId(
        accountId = accountId,
        month = date.month(),
        year = date.year(),
    )

    @MockK
    lateinit var commandBus: CommandBus

    @BeforeEach
    fun setUpEach() {
        fixture = SagaTestFixture(LedgerLifecycleSaga::class.java)
        fixture.registerCommandGateway(CommandGateway::class.java)
    }

    @Test
    fun registerAccount() {
        fixture
            .givenNoPriorActivity()
            .whenAggregate(accountId.toString())
            .publishes(
                NewAccountRegisteredEvent(
                    accountId = accountId,
                    bankName = BCP,
                    name = name,
                    accountType = CHECKING,
                    startingBalance = balance,
                    startingBalanceDate = date,
                    currency = EUR,
                    notes = "",
                )
            )
            .expectSuccessfulHandlerExecution()
            .expectActiveSagas(0)
            .expectDispatchedCommands(
                OpenBalanceForMonthCommand(
                    ledgerId = ledgerId,
                    startBalance = balance,
                )
            )
    }
}

The null value is actually expected if the command handler resolves successfully without any reply.
So, I you have a command handler like so:

public void handle(MyCommand command) {
    // perform business logic...
    // publish 0-N events...
}

In the above case, the return type is void, so null is returned. That is, actually, expected behavior. I was, frankly, surprised that the CommandGateway states this isn’t the case…so I took a look whats written down. The only reference to null is the following sentence:

When the thread is interrupted, this method returns {@code null}.

Hence, in case of an interrupt, null is returned.

A little bit before that, the description states the following:

The result of the execution is returned when available.

That, essentially, means that whatever the command handler returns, will be returned. So, in case of void, null would be returned. Granted, I don’t think that’s overly clear. As such, I’ve made a commit (which you can find here to clarify this behavior.

That said, you were thus misled by the documentation. My apologies for that, @mumia!