Java 8 has the addition of the Clock, which can be used as a time provider for effective stubbing when test writing.
Output code R031 means that Diffblue Cover was only able to write tests that are time-sensitive. This may mean that the tests do not run at an alternative time. Using a java.util.Clock instance allows the time to be parameterized during testing, as shown in the example below:
Consider the following example of a class modelling the loan of a library book, including an isOverdue() method to workout whether the book needs to be returned:
package com.example;
import java.time.LocalDate;
import java.util.Objects;
public class LibraryBookLoan {
public final String bookName;
public final LocalDate startDate;
public LibraryBookLoan(String bookName, LocalDate startDate) {
this.bookName = Objects.requireNonNull(bookName);
this.startDate = Objects.requireNonNull(startDate);
}
public LocalDate getDueDate() {
return startDate.plusDays(10);
}
public boolean isOverdue() {
final LocalDate today = LocalDate.now();
return today.isAfter(getDueDate());
}
}
When Diffblue Cover writes tests for this class you can see that we get a R031 indicating that the isOverdue() method is time sensitive, making it difficult to test.
Creating tests:
---------------
[1/3] com.example.LibraryBookLoan.<init>
[1/3] Complete tests created: 1
[2/3] com.example.LibraryBookLoan.getDueDate
[2/3] Complete tests created: 1
[3/3] com.example.LibraryBookLoan.isOverdue
[3/3] R031: Method may be time-sensitive
[3/3] Diffblue Cover was only able to write assertions which were time-sensitive.
[3/3] The assertions no longer passed when run at an alternate date, time and
[3/3] timezone. Try refactoring the method to take a java.util.Clock instance so
[3/3] that the time can be parameterized during testing.
The recommendation here is to refactor the isOverdue() method to take a java.time.Clock parameter:
package com.example;
import java.time.Clock;
import java.time.LocalDate;
public class LibraryBookLoan {
public final String bookName;
public final LocalDate startDate;
public LibraryBookLoan(String bookName, LocalDate startDate) {
this.bookName = bookName;
this.startDate = startDate;
}
public LocalDate getDueDate() {
return startDate.plusDays(10);
}
public boolean isOverdue(Clock clock) {
final LocalDate today = LocalDate.now(clock);
return today.isAfter(getDueDate());
}
}
Sometimes it's not acceptable to change all callers, and the old interface needs to be maintained. If that's the case then the original time-sensitive signature can be maintained and a parameterized one can be added:
package com.example;
import java.time.Clock;
import java.time.LocalDate;
public class LibraryBookLoan {
public final String bookName;
public final LocalDate startDate;
public LibraryBookLoan(String bookName, LocalDate startDate) {
this.bookName = bookName;
this.startDate = startDate;
}
public LocalDate getDueDate() {
return startDate.plusDays(10);
}
public boolean isOverdue() {
return isOverdue(Clock.systemDefaultZone());
}
boolean isOverdue(Clock clock) {
final LocalDate today = LocalDate.now(clock);
return today.isAfter(getDueDate());
}
}
This way you still get the R031 output codes for the isOverdue() method, but the underlying logic can be tested via the isOverdue(Clock) method.