Skip to content

Commit

Permalink
Merge pull request #1772 from pragtics/master
Browse files Browse the repository at this point in the history
Added an option to create a fixture for a state stored aggregate
  • Loading branch information
smcvb committed May 12, 2021
2 parents 332288a + db3ea8b commit 5fba136
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.axonframework.commandhandling.GenericCommandMessage;
import org.axonframework.commandhandling.SimpleCommandBus;
import org.axonframework.common.Assert;
import org.axonframework.common.AxonConfigurationException;
import org.axonframework.common.ReflectionUtils;
import org.axonframework.common.Registration;
import org.axonframework.deadline.DeadlineMessage;
Expand Down Expand Up @@ -123,6 +124,7 @@ public class AggregateTestFixture<T> implements FixtureConfiguration<T>, TestExe
private final EventStore eventStore;
private final List<FieldFilter> fieldFilters = new ArrayList<>();
private final List<Object> resources = new ArrayList<>();
private boolean useStateStorage;
private RepositoryProvider repositoryProvider;
private IdentifierValidatingRepository<T> repository;
private final StubDeadlineManager deadlineManager;
Expand Down Expand Up @@ -166,6 +168,12 @@ public final FixtureConfiguration<T> withSubtypes(Class<? extends T>... subtypes
return this;
}

@Override
public FixtureConfiguration<T> useStateStorage() {
this.useStateStorage = true;
return this;
}

@Override
public FixtureConfiguration<T> registerRepository(Repository<T> repository) {
this.repository = new IdentifierValidatingRepository<>(repository);
Expand Down Expand Up @@ -306,21 +314,20 @@ public TestExecutor<T> andGiven(Object... domainEvents) {

@Override
public TestExecutor<T> givenNoPriorActivity() {
return given(Collections.emptyList());
ensureRepositoryConfiguration();
clearGivenWhenState();
return this;
}

@Override
public TestExecutor<T> givenState(Supplier<T> aggregate) {
if (this.repository == null) {
this.useStateStorage();
}

ensureRepositoryConfiguration();
clearGivenWhenState();
DefaultUnitOfWork.startAndGet(null).execute(() -> {
if (repository == null) {
registerRepository(new InMemoryRepository<>(aggregateType,
subtypes,
eventStore,
getParameterResolverFactory(),
getHandlerDefinition(),
getRepositoryProvider()));
}
try {
repository.newInstance(aggregate::get);
} catch (Exception e) {
Expand All @@ -341,6 +348,11 @@ public TestExecutor<T> given(List<?> domainEvents) {

@Override
public TestExecutor<T> andGiven(List<?> domainEvents) {
if (this.useStateStorage) {
throw new AxonConfigurationException(
"Given events not supported, because the fixture is configured to use state storage");
}

for (Object event : domainEvents) {
Object payload = event;
MetaData metaData = null;
Expand Down Expand Up @@ -506,16 +518,29 @@ private void registerAggregateCommandHandlers() {
}

private void ensureRepositoryConfiguration() {
if (repository == null) {
if (repository != null) {
return;
}

if (this.useStateStorage) {
this.registerRepository(new InMemoryRepository<>(
aggregateType,
subtypes,
eventStore,
getParameterResolverFactory(),
getHandlerDefinition(),
getRepositoryProvider()));
} else {
AggregateModel<T> aggregateModel = aggregateModel();
registerRepository(EventSourcingRepository.builder(aggregateType)
.aggregateModel(aggregateModel)
.aggregateFactory(new GenericAggregateFactory<>(aggregateModel))
.eventStore(eventStore)
.parameterResolverFactory(getParameterResolverFactory())
.handlerDefinition(getHandlerDefinition())
.repositoryProvider(getRepositoryProvider())
.build());
this.registerRepository(EventSourcingRepository.builder(aggregateType)
.aggregateModel(aggregateModel)
.aggregateFactory(new GenericAggregateFactory<>(
aggregateModel))
.eventStore(eventStore)
.parameterResolverFactory(getParameterResolverFactory())
.handlerDefinition(getHandlerDefinition())
.repositoryProvider(getRepositoryProvider())
.build());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,15 @@ public interface FixtureConfiguration<T> {
*/
FixtureConfiguration<T> withSubtypes(Class<? extends T>... subtypes);

/**
* Configures the fixture for state stored aggregates.
* This will register an in-memory {@link org.axonframework.commandhandling.model.Repository} with this fixture.
* Should be used before calling {@link FixtureConfiguration#givenState(Supplier)} or {@link FixtureConfiguration#givenCommands(List)} (Supplier)}.
*
* @return the current FixtureConfiguration, for fluent interfacing
*/
FixtureConfiguration<T> useStateStorage();

/**
* Registers an arbitrary {@code repository} with the fixture. The repository must be wired
* with the Event Store of this test fixture.
Expand Down Expand Up @@ -323,8 +332,8 @@ FixtureConfiguration<T> registerDeadlineHandlerInterceptor(
TestExecutor<T> givenState(Supplier<T> aggregateState);

/**
* Indicates that no relevant activity has occurred in the past. The behavior of this method is identical to giving
* no events in the {@link #given(java.util.List)} method.
* Indicates that no relevant activities like commands or events have occurred in the past.
* This also means that no previous state is present in the repository.
*
* @return a TestExecutor instance that can execute the test with this configuration
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
package org.axonframework.test.aggregate;

import org.axonframework.commandhandling.CommandHandler;
import org.axonframework.common.AxonConfigurationException;
import org.axonframework.messaging.unitofwork.CurrentUnitOfWork;
import org.axonframework.modelling.command.AggregateIdentifier;
import org.axonframework.modelling.command.TargetAggregateIdentifier;
import org.junit.jupiter.api.*;

import java.util.Collections;
import java.util.function.Supplier;

import static org.axonframework.modelling.command.AggregateLifecycle.apply;
Expand Down Expand Up @@ -61,6 +63,25 @@ void testCreateStateStoredAggregate() {
.expectState(aggregate -> assertEquals("message2", aggregate.getMessage()));
}

@Test
void testGivenCommandsForStateStoredAggregate() {
fixture.useStateStorage()
.givenCommands(new InitializeCommand(AGGREGATE_ID, "message"))
.when(new SetMessageCommand(AGGREGATE_ID, "message2"))
.expectEvents(new StubDomainEvent())
.expectState(aggregate -> assertEquals("message2", aggregate.getMessage()));
}


@Test
void testCreateStateStoredAggregateWithCommand() {
fixture.useStateStorage()
.givenNoPriorActivity()
.when(new InitializeCommand(AGGREGATE_ID, "message"))
.expectEvents(new StubDomainEvent())
.expectState(aggregate -> assertEquals("message", aggregate.getMessage()));
}

@Test
void testEmittedEventsFromExpectStateAreNotStored() {
fixture.givenState(() -> new StateStoredAggregate(AGGREGATE_ID, "message"))
Expand Down Expand Up @@ -105,6 +126,26 @@ void testStateStoredAggregateCanAttachRegisteredResource() {
verify(testResource).difficultOperation(expectedMessage);
}

@Test
void testGivenWithStateStorageException() {
fixture.useStateStorage();

assertThrows(
AxonConfigurationException.class,
() -> fixture.given(new StubDomainEvent())
);
}

@Test
void testGivenWithEventListAndStateStorageExpectException() {
fixture.useStateStorage();

assertThrows(
AxonConfigurationException.class,
() -> fixture.given(Collections.singletonList(new StubDomainEvent()))
);
}

private static class InitializeCommand {

private final String id;
Expand Down Expand Up @@ -200,6 +241,7 @@ public static class StateStoredAggregate {
@CommandHandler
public StateStoredAggregate(InitializeCommand cmd) {
this.id = cmd.getId();
this.message = cmd.getMessage();
apply(new StubDomainEvent());
}

Expand Down

0 comments on commit 5fba136

Please sign in to comment.