Fixed Clock, testing and injecting in Axon 3

681 views
Skip to first unread message

Dominic Heutelbeck

unread,
Sep 16, 2016, 5:44:10 PM9/16/16
to Axon Framework Users
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



Dominic Heutelbeck

unread,
Sep 16, 2016, 6:34:25 PM9/16/16
to Axon Framework Users

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 :-) 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

Patrick Haas

unread,
Sep 17, 2016, 4:03:19 PM9/17/16
to Axon Framework Users
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

Dominic Heutelbeck

unread,
Sep 17, 2016, 6:25:47 PM9/17/16
to Axon Framework Users
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.

From my point of view the other options are starting to sound increasingly like hacks to get around the current limitations. 

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

Steven Grimm

unread,
Sep 17, 2016, 11:52:14 PM9/17/16
to axonfr...@googlegroups.com
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

September 17, 2016 at 3:25 PM
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.

From my point of view the other options are starting to sound increasingly like hacks to get around the current limitations. 

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

Am Samstag, 17. September 2016 22:03:19 UTC+2 schrieb Patrick Haas:
--
You received this message because you are subscribed to the Google Groups "Axon Framework Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to axonframewor...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
September 17, 2016 at 1:03 PM
--
You received this message because you are subscribed to the Google Groups "Axon Framework Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to axonframewor...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
September 16, 2016 at 3:34 PM

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 :-) 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

Am Freitag, 16. September 2016 23:44:10 UTC+2 schrieb Dominic Heutelbeck:
 
--
You received this message because you are subscribed to the Google Groups "Axon Framework Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to axonframewor...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
September 16, 2016 at 2:44 PM
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



--
You received this message because you are subscribed to the Google Groups "Axon Framework Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to axonframewor...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Dominic Heutelbeck

unread,
Sep 18, 2016, 8:43:35 AM9/18/16
to Axon Framework Users
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

Allard Buijze

unread,
Sep 19, 2016, 9:07:37 AM9/19/16
to Axon Framework Users
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

Dominic Heutelbeck

unread,
Sep 19, 2016, 3:16:09 PM9/19/16
to Axon Framework Users
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<Object> 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<Object> 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<T> implements FixtureConfiguration<T>, 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<T> implements FixtureConfiguration<T>, 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

Dominic Heutelbeck

unread,
Sep 19, 2016, 3:17:01 PM9/19/16
to Axon Framework Users
Just a note. I have not tested the proposed fix. Its just highlighting what I suspect is going on.

Dominic Heutelbeck

unread,
Sep 19, 2016, 3:21:16 PM9/19/16
to Axon Framework Users
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?

Allard Buijze

unread,
Sep 19, 2016, 11:19:58 PM9/19/16
to Axon Framework Users
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
Reply all
Reply to author
Forward
0 new messages