Fixed Clock, testing and injecting in Axon 3

Hello,

I am currently trying to migrate a small code base from Axon 2.4 to 3.0-M4.

Orverall this is pretty smooth.

Apparantly, the dependency on joda time is gone in favour for java.time.

So previously you would use DateTimeUtils.setCurrentMillisFixed() in testing.

Is there already a best practice for testing aggregates which emit time based events?

Searching for the problem in general, I find, that it is recommended to add a Clock.fixed object to the dependencies of the object under test.

How would I need to configure a spring application to inject a clock ? Also how does this relate to the lifecycle of the aggregate?
For example, using a command handler constructor, the injected Clock is not yet available in the constructor.

Currently this appears to create quite some overhead compared to the simplicity of the setCurrentMillisFixed approach.

Does anybody have expreiences, best practices, code samples ?

The axon bank example on github currently avoids time issues.

Thanks for your help.

Best regards,
Dominic

So basically what I was trying to do was very similar to a password reset token with a limited time to live. So the Command “Request Reset” would create a token “(secret, expiry date)”. Resulting in an event “reset requested (secret, expiry date)”. And the aggregate calculated the expiry date by “Instant.now().plus(SOME DURATION)”.

So I refactored this to generating an event “reset requested (secret, SOME DURATION)” and I injected the event timestamp into the command handlers (e.g., a matching saga) which then can calculate the expiry date locally.

For testing of the aggregate this eliminates the need for fixing the time … for now.

I actually consider this a better design from the design of the events point of view.

There are however some drawbacks:

  • For now I have not verified, if the test fixture actually keeps all timestamps constant with regards to the fixture creation time. At least the 2.4 docs say so.

  • The fixture has no public method which allows me to access the time it has stored. So I cannot use it reliably in tests for checking timestamps.

There is one scenario which is not critical for my use-case, where I had to drop a small feature and related unit tests.

If a command reaches the aggregate after the timeout, it should refuse the command.

I have not found a way in the aggregate fixture to advance time. So this I cannot test this behaviour.

It wouldn’t be necessary to actually provide the test fixture with similar methods as the saga fixture. In the aggregate case, it would be sufficient, if:

  • I would know the timestamp used by the fixture for new events (see above)

  • I could manually set the timestamp of the fixture.given(event) events. So I could put these into the past.

Following the idea of only using timestamp-relative event data, together with the last two points would probably eliminate most cases which require a fixed clock for testing.

The testing fixture timestamp handling improvements would be a small feature request from my side :slight_smile: Or maybe I have just not found out how to do it.

I would be interested in your opinions on this issue.

Best regards,
Dominic

Hi Dominic,

This may not apply to your specific issue, but I’ve some experience with similar issues:

When I’ve had to deal with time, I’ve always abstracted it to some easily mock/stub-able interface:

interface TimeQueryService {
DateTime now();
}

You’d simply provide a dummy implementation to your tests (e.g. pass into constructor, add as registerInjectableResource, etc). The dummy implementation has a setter and a getter, so you can manually advance time in between test steps. In my spring context I’d configure a TimeQueryServiceImpl that always returns e.g. DateTime.now();

Using traditional tests, you’d write something like:

  • timeStub.setTime(“2016-01-01”,“00:00”) // or whatever format works
  • x = component.prepareSomething()
  • timeSTub.setTime(“2016-02-01”, “00:00”)
  • component.attemptToUse(x);

With a “mock”, you can generally also configure the replay behavior to return different results on the first, second, nth invocation.

When you’re using aggregate test fixtures, it can be a little tricky to insert code like “timeStub.setTime(…)” when you’re giving a number of commands:

fixture.givenCommands(a, b, /* change time */, c, d).when(e).expect…

One solution for dealing with this that’s worked well for me is to define a test-only command and handler that lets me modify the test stubs in between comands that are executing:

/**
 * Inject arbitrary test/stub setup tasks into a stream of <code>givenCommands</code>.
 * <p>Fixture configuration:
 * <pre><code>
 * fixture.registerCommandHandler(TestSetupCommand.class, TestSetupCommand.HANDLER)
 * </code></pre>
 * <p>
 * Usage:
 * <pre><code>
 *     fixture.givenCommands(
 *          command1,
 *          command2,
 *          TestSetupCommand.execute(() -> stub.configureValue("FOO")),
 *          command3
 *     )...
 * </code></pre>
 */
public interface TestSetupCommand {

   void run();

   CommandHandler<TestSetupCommand> HANDLER = new CommandHandler<TestSetupCommand>() {
      @Override
      public Object handle(CommandMessage<TestSetupCommand> commandMessage, UnitOfWork unitOfWork) throws Throwable {
         commandMessage.getPayload().run();
         return Void.TYPE;
      }
   };

   static CommandMessage<TestSetupCommand> execute(TestSetupCommand task) {
      return new GenericCommandMessage<>(TestSetupCommand.class.getCanonicalName(), task, MetaData.emptyInstance());
   }

}


~Patrick

Thank you for your comments and examples. These are very reasonable approaches.
Also I think the semantics of controlling the timestamps of the test fixture in the sense that you could say something like the follwoing would result in very readable tests. With the mock you have the testcase all over the place of your testclass.

fixture.givenEventAt(event1,instant1).givenEventAt(event2,instant2).whenCommandAt(command,instant).expect…

or:
fixture.givenInstant(instant1).givenEvent(event1,event2).givenInstant(instant2).whenCommand(command)…

(maybe this is easy to implement by extending the existing fixture)

Here it is absolutely obvious what is happening. But it requires that you let go of accessing the current time directly in your aggrgate and you rely on the timestamp mechanism. I would also require Axon to be able to inject not only event timestamps with @Timestamp, but also command timestamps (because decsions on state change may depend on the time). I am not sure which implications enabling this would have.

Your approach ist aiming exactly at what I was describing and achives such a behaviour. Very cool. Still it would be nice to be able to do this out of the box with the fixture.

Thanks.
Dominic

Maybe this could be modeled as described briefly in the Axon manual (the “Keeping track of deadlines” section), where a saga is responsible for sending an “expire the password reset token” command to the aggregate after a certain amount of time has passed without any activity on the pending request.

This would make testing pretty simple using Axon’s existing test fixtures without any need to use mock clocks. The saga test would use AnnotatedSagaTestFixture’s “whenTimeElapses” method to make sure that an appropriate command is sent after a certain amount of time and isn’t sent if the aggregate publishes events indicating that the expiration isn’t needed. And the aggregate test would make sure that, upon receiving the expiration command, it properly invalidates the password reset token. Basically, you’d be separating the “When should the token expire?” logic from the “What should happen when the token expires?” logic and testing them independently.

In my application I use this pattern pretty extensively and it’s generally been very test-friendly. My application has a lot of time-sensitive business logic but the aggregates don’t ever manipulate time values. The only situation where I usually end up having to mock clocks is when I’m testing the code that populates my query model (which contains “created time” fields that the test needs to verify), and in those places I inject a Clock instance.

-Steve

Hello, Steve.

Yes. This is exactly what I am doing. I just had an additional check in place in the aggregate which validated the timestamp vs. the deadline of the token. This is pretty much irrelevant as in this use-case; this little bit of race condition (token ist used some milliseconds before the saga invalidates the token) is absolutely non-critical. I could imagine other scenarios. Also there are certainly other use-cases which require some temporal logic within the aggregate itself.

Dominic

Hi,

the time of the EventMessages is taken from the clock defined in GenericEventMessage.clock. It is a public, static , non-final field which defaults to UTC.
In test cases, you can set the clock however you want by updating that field.
Don’t forget to update it back after the test…

While the recommended approach has always been to use sagas for deadlines, we’re also seeing very elegant solutions where the aggregate itself also has a watch. We’re looking into a Deadlines API, which can be used from aggregates as well. Sometimes, a Saga just for a payment deadline, for example, feels a bit ‘synthetic’.

Cheers,

Allard

Hello, Allard,

Thanks for that pointer. This may have pointed me to discover a bug while testing this method of testing.

however, When I do:

@Before
public void setUp() {
GenericEventMessage.clock = Clock.fixed(Instant.ofEpochSecond(0L), ZoneId.systemDefault());
fixture = new AnnotatedSagaTestFixture<>(Clazz.class);
}

@After
public void tearDown() {
GenericEventMessage.clock = Clock.systemUTC();
}

The timestamps of the messages in the Saga (same when testing an aggregate) will still use the system clock. However, when I use the static message factory method, the time is set by the new clock:

EventMessage message = GenericEventMessage.asEventMessage(Boolean.FALSE);
log.info(“Message time : {}”, message.getTimestamp());

logs:
19:39:51.862 INFO o.o.a.command.account.AccountTest setUp 86 - Message time : 1970-01-01T00:00:00Z

Even, if I manually create the events with the factory class …

EventMessage message2 = GenericEventMessage.asEventMessage(event2);

The call
fixture.given(message2)…

Results in the @Timestamp in the aggregate event handler to return the current system time.

Maybe I have found a bug here…

I think this finally ends up to in:

public class GivenWhenThenTestFixture implements FixtureConfiguration, TestExecutor {

@Override
public TestExecutor given(List<?> domainEvents) {
ensureRepositoryConfiguration();
clearGivenWhenState();
try {
for (Object event : domainEvents) {
Object payload = event;
MetaData metaData = null;
if (event instanceof Message) {
payload = ((Message) event).getPayload();
metaData = ((Message) event).getMetaData();
}
this.givenEvents.add(new GenericDomainEventMessage<>(
aggregateType.getSimpleName(), aggregateIdentifier, sequenceNumber++, payload, metaData));
}
} catch (Exception e) {
FixtureResourceParameterResolverFactory.clear();
}
return this;
}

Here you can see, that the timestamp is ignored regardless of if this is instanceof Message or not. The constructor (new GenericDomainEventMessage<>() isthen calling Instant.now() to set the timesamp.
In this method should proably read something like this:

public class GivenWhenThenTestFixture implements FixtureConfiguration, TestExecutor {

@Override
public TestExecutor given(List<?> domainEvents) {
ensureRepositoryConfiguration();
clearGivenWhenState();
try {
for (Object event : domainEvents) {
Object payload = event;
MetaData metaData = null;

Instant timestamp = null;

if (event instanceof Message) {
payload = ((Message) event).getPayload();
metaData = ((Message) event).getMetaData();
timestamp= ((Message) event).getTimestamp();
} else {
timestamp = Instant.now()
}
this.givenEvents.add(new GenericDomainEventMessage<>(
aggregateType.getSimpleName(), aggregateIdentifier, sequenceNumber++, payload, metaData,timestamp));
}
} catch (Exception e) {
FixtureResourceParameterResolverFactory.clear();
}
return this;
}

Right ? (Sorry for my lack of making nicely formatted sourcecode here)

If so, We would be able to do eactly what I previously requested and generate individual events with different timestamps manually to model the time dependent history of an aggregate for testing. Neato!

Thanks again.

Dominic

Just a note. I have not tested the proposed fix. Its just highlighting what I suspect is going on.

Another note. Putting the clock object into the message factory appears to be oddly specific. Shouldn’t that be a global clock setting?
Something like AxonClockConfiuration.clock. So that any code in Axon can use the central clock. The code I mention should not access the Instant.now() clock if we want the capability of setting a static clock for testing.
Setting it should affect all locations which access a clock at all. Or am I mistaken here?

Hi,

a generic clock location probably makes sense, but for now, the event is the only place where a clock is used.

But the issue with the saga test fixture isn’t a bug. The saga fixture modifies the clock itself. It sets it to system time, but stops the clock. So no time passes between messages, unless you specifically specify that. There are methods on the fixture to pass time. You should use those instead.

I probably (mistakenly) understood that you were using time in your aggregate.

Cheers,

Allard