Learn more about Velocity Partners offshore software development company
My last article reviewed some techniques to test time-related code in a legacy app. Today I’d like to share strategies for writing date-handling code and discuss methods for testing it using Java 8.
Imagine that you need to write a class that shows a welcome message based on the time that has passed since the user last logged into the system. Here’s the requirement:
Time since last login | Greeting message |
---|---|
<=10 minutes | “Hello, I haven’t see you in a few minutes!” |
> 10 mins, < 24 hours | “Hello, I haven’t see you in a while!” |
>=24 hours < 48 hours | “Hello, I haven’t see you since yesterday!” |
>=48 hours | “Hello, I haven’t see you in a long time!” |
Writing classes that handle date/time
Anytime you create a class in your application that works with time, declare and inject a Java 8 Clock
to provide access to the current date/time information. For example:
public class GreetingsMessage { private Clock clock; public GreetingsMessage(Clock clock) { this.clock = clock; } }
Several options are available for managing time zones, depending on the application’s needs. Initializing the clock with Clock.systemDefaultZone();
will join the time zone of the system. Alternately, if the application will be working in multiple time zones, Clock.system(ZoneId zone);
responds to each user’s time zone and adjusts to that region.
Once the clock is established, create the date and time passing the clock as a parameter:
LocalDateTime currentDateTime = LocalDateTime.now(clock);
All methods in the new API to retrieve the current date or time have an overloaded version that receives the clock as a parameter.
Preparing for testing
Now, prepare the test for the greeting message. Testing requires fixed inputs so that the user can assert business rules based on a fixed time. (Relying on the system’s time for testing will produce brittle tests that don’t work consistently.) Here’s the code:
public class GreetingsMessageTest { private GreetingsMessage greetingsMessage; private final LocalDateTime REFERENCE_DATE_TIME = LocalDateTime.of(2016, 4, 1, 10, 0); //2016-04-01 at 10:00am private final ZoneId defaultZone = ZoneId.systemDefault(); private final Clock FIXED_CLOCK = Clock.fixed(REFERENCE_DATE_TIME.atZone(defaultZone).toInstant(), defaultZone); @Before public void setup(){ greetingsMessage = new GreetingsMessage(FIXED_CLOCK); } }
Here the reference date and time has been defined as 10 a.m. on April 1, 2016. Because this is a test, it’s okay to use the default time zone to build the clock. The FIXED_CLOCK constant contains the fixed clock at the reference date time in the default zone. Now, every time the code calls LocalDateTime.now(clock)
it will pick up 10 a.m. on April 1, 2016. That’s deterministic enough to write tests.
Writing tests for time
The first test will run a scenario in which the user last logged in five minutes ago.
@Test public void testGreetingSinceMinutesAgo(){ //Given LocalDateTime lastSeen = REFERENCE_DATE_TIME.minus(5, ChronoUnit.MINUTES); //When String greeting = greetingsMessage.getMessage(lastSeen); //Then assertEquals("Hello, I haven't see you in a few minutes!", greeting); }
The lastSeen
parameter is derived from the reference time set up in the test. This case uses 9:55 a.m. on April 1, 2016 — five minutes before the reference time. describes methods to help calculate a moment in time starting from the current value, so be sure to check the API to leverage those on your tests.
You can also test the “yesterday” scenario using a different method to subtract a given number of days from the reference date:
@Test public void testGreetingSinceYesterday(){ //Given LocalDateTime lastSeen = REFERENCE_DATE_TIME.minusDays(1); //When String greeting = greetingsMessage.getMessage(lastSeen); //Then assertEquals("Hello, I haven't see you since yesterday!", greeting); }
This example yields a value of 10 a.m. on March 31, 2016 for lastSeen
. You could create this value directly, but I think the API helps with the readability of the condition of this test.
Ultimately, you should be able to implement time testing with these steps. The complete example is posted .
Happy time testing with Java 8!