What would be the best way to something like this where a timestamp is updated automatically before making the mocked call?
Here is some dummy code of what I am trying to test:
public class ThingWithATimestamp {
public Long timestamp;
public String name;
public ThingWithATimestamp(String name) {
this.name = name;
}
}
public class TheClassThatDoesStuff {
private ThingConnector connector;
public TheClassThatDoesStuff(ThingConnector connector) {
this.connector = connector;
}
public void updateTheThing(MyThingWithATimestamp thing) {
thing.timestamp = currentTimestamp();
connector.update(thing);
}
}
Here is what I want to test:
public class TheClassThatDoesStuffTests {
@Test
public void canUpdateTheThing() {
ThingConnector connector = mock(ThingConnector.class);
TheClassThatDoesStuff doer = new ThisClassThatDoesStuff(connector);
doer.updateTheThing(new ThingWithATimestamp("the name"));
verify(connector, times(1)).update(SomeMatcherThatICantFigureOut);
}
I know this code is pretty dumbed down but I think it accurately portrays what I am trying to verify. I basically need a matcher to fill in the test to verify that the timestamp is within X of the current time so I know it got updated correctly and that connector.update
was called with the proper timestamp for the object.
First:
Second:
I find the most robust way to deal with time-critical code is to wrap up all of your time-critical functions in their own class. I usually call it
TimeHelper
. So this class might look like the following.and it might have more methods of the same type. Now, any class that uses such functions should have (at least) two constructors - the normal one that you'll use in your application, plus a package-private one in which a
TimeHelper
is a parameter. ThisTimeHelper
needs to be stored away for later use.Now, within your class, instead of writing
System.currentTimeMillis()
, writetimeHelper.currentTimeMillis()
. This will, of course, have exactly the same effect; except now, your class has magically become much more testable.When you test your class, make a mock of
TimeHelper
. Configure this mock (using Mockito'swhen
andthenReturn
, or alternativelydoReturn
) to return any time values you like - whatever you need for your test. You can even return multiple values here, if you're going to have multiple calls tocurrentTimeMillis()
in the course of the test.Now use the second constructor to make the object that you're going to test, and pass in the mock. This gives you perfect control of what time values will be used in the test; and you can make your assertions or verifications assert that precisely the right value has been used.
If you do this, you know that your test will always work, and never be dependent on the time slicing policies of your operating system. You also have the power to verify the exact values of your timestamps, rather than asserting that they fall within some approximate interval.