Check for markDeleted in AggregateTestFixture

Hi,

I have a question about the AggregateTestFixture from Axon. I understand that I can send commands and check whether the aggregate applied certain events. However, I wonder if there is a way to check whether an aggregate has actually been deleted. Surely I can do something like the following:

`

fixture.given(new AccountCreatedEvent(…))
.when(new DeleteAccountCommand(…))
.expectSuccessfulHandlerExecution()
.expectEvents(new AccountDeletedEvent(…));

`

Now I checked that my Account aggregate applied an AccountDeletedEvent, but is there a way to make sure that the aggregate called markDeleted? It seems to me like a normal use case, but I cannot find anything in the documentation. Did I just miss the right method from the fixture or is there a reason why this is usually not checked?

Thank you and best regards,

Nils

H Nils,

I genuinely thought we had an issue for this, but I couldn’t find it on GitHub.
So I wanted to point you to the issue and say something like “stay tuned”, but it seems some more work is required to get this in.

Regardless, I think your statement is fair.

It would be beneficial if the ResultValidator class has a method like expectDeleted()/expectMarkedDeleted(), so that an Aggregate’s lifecycle end can be verified in the test cases.

Would you have the time to draft a small issue for this in the issue tracker, Nils?
And, if you are up for the task, even provide a PR to contribute on this?

Any how, thanks for bringing this up, I think it’s a nice addition to the Test Fixtures.

Cheers,
Steven

Hi,

I have a related question: when testing the delete method/command of the aggregate, my testcase succeeds, but I get:

AggregateDeletedException: Aggregate with identifier [468f80d7-50e4-4dd3-9406-4c2eaf97c485] not found. It has been deleted.

It’s actually stated in the reference guide, that Axon tries to detect illegal state changes during fixture tests:
https://docs.axoniq.io/reference-guide/implementing-domain-logic/command-handling/testing#test-execution-phase
So in this case I’ve tried to set:

fixture.setReportIllegalStateChange(false)

That made the exception go away, but I’m wondering why it occurred in the first place, is it normal/expected, or I’m doing something wrong/missing
from that specific test case?

Thanks,
Regards

Hi Nils & Vilmos,

Firstly, I wrongly thought this wasn’t implemented yet.
One of the contributors actually build this feature, provided in this PR.
It’ll be part of Axon Framework 4.2, so stay tuned for that.

Secondly, Vilmos, would you mind sharing the way you are using the test fixture, so an actual snippet?
I wouldn’t disable the report illegal state changes to be honest, as typically you do want to ensure that state changes only happen in event sourcing handlers.

Note worthy is, that you should mark an Aggregate as deleted in an Event Sourcing Handler.
This is required, as even though an Aggregate is marked deleted, this knowledge should be retrievable when sourcing it from it’s Events.
Thus, this should be regarded as a state change which should not occur in a Command Handler.

Cheers,
Steven

Hi Steven,

It happens in the most simple cases as well, like:

@CommandHandler private fun delete(command: DeleteMyAggregateCommand) = apply(MyAggregateDeletedEvent(myAggregateId))

@EventSourcingHandler private fun on(event: MyAggregateDeletedEvent) = markDeleted()

and in the test case:

val fixture = AggregateTestFixture<MyAggregate>(MyAggregate::class.java) fixture.given(myAggregateCreatedEvent) .when(DeleteMyAggregateCommand(myAggregateId)) .expectEvents(MyAggregateDeletedEvent(myAggregateId)

but I think everything is as designed, in AggregateTestFixture#detectIllegalStateChanges there is an

AxonAssertionError if there is state mismatch for delete, and test indeed succeeds, the exception is coming from the EventSourcingRepository:

org.axonframework.eventsourcing.AggregateDeletedException: Aggregate with identifier [2c0b80f8-9d27-4159-a4e3-d0372a282418] not found. It has been deleted.
at org.axonframework.eventsourcing.EventSourcingRepository.doLoadWithLock(EventSourcingRepository.java:129)
at org.axonframework.eventsourcing.EventSourcingRepository.doLoadWithLock(EventSourcingRepository.java:50)
at org.axonframework.modelling.command.LockingRepository.doLoad(LockingRepository.java:118)
at org.axonframework.modelling.command.LockingRepository.doLoad(LockingRepository.java:52)
at org.axonframework.modelling.command.AbstractRepository.lambda$load$4(AbstractRepository.java:122)
at java.util.HashMap.computeIfAbsent(HashMap.java:1127)
at org.axonframework.modelling.command.AbstractRepository.load(AbstractRepository.java:121)
at org.axonframework.modelling.command.AbstractRepository.load(AbstractRepository.java:146)
at org.axonframework.test.aggregate.AggregateTestFixture.detectIllegalStateChanges(AggregateTestFixture.java:504)
at org.axonframework.test.aggregate.AggregateTestFixture.when(AggregateTestFixture.java:422)
at org.axonframework.test.aggregate.AggregateTestFixture.when(AggregateTestFixture.java:405)

so I guess this is expedted, it’s just a bit wierd, I thought test fixture uses some special repository for testing or something,
but since the exception has nothing to do with the test, I’m putting back the state change detection as you advised, thanks!

Cheers

Hi Vilmos,

The Test Fixtures do indeed use the EventSourcingRepository internally, granted that you have written your Aggregate to utilize Event Sourcing.

If you take the State-Stored Aggregate route, the used repository will become a InMemoryRepository, which will be triggered if you use the FixtureConfiguration#givenState(Supplier<T>) method.

It’s the storage mechanism, the EventStore which is of a specific type called the RecordingEventStore.

Thus your assumption on the fact the Test Fixtures use ‘some special implementations’ is correct, just not on every component. :slight_smile:

Added, the stack trace you’ve shared point out an ‘illegal state change’ was detected after the when operation was performed.
As you are Event Sourcing your Aggregate, it is a strict requirement to not perform any state changes on handling a command.

Adjusting the Aggregate to be deleted through the AggregateLifecycle#markDeleted() method is however a state change, which thus should be performed in an Event Sourcing Handler.

If this was not enforced you could have the scenario that on another Command coming in for the given Aggregate that was marked deleted, that it would be Sourced from it’s Events without knowing it was actually marked as deleted.

Hope this clarifies some things Vilmos.

Cheers,
Steven