我将介绍一个场景,我们应该使用而不是 .Supplier<LocalDate>
LocalDate
直接调用静态方法的代码(如)很难进行单元测试。考虑一个场景,我们想要对计算一个人的年龄的方法进行单元测试:LocalDate.now()
getAge()
class Person {
final String name;
private final LocalDate dateOfBirth;
Person(String name, LocalDate dateOfBirth) {
this.name = name;
this.dateOfBirth = dateOfBirth;
}
long getAge() {
return ChronoUnit.YEARS.between(dateOfBirth, LocalDate.now());
}
}
这在生产中工作正常。但是,单元测试要么必须将系统的日期设置为已知值,要么每年更新一次,以期望返回的年龄增加一个,这两者都是非常令人敬畏的解决方案。
更好的解决方案是让单元测试注入已知日期,同时仍然允许生产代码使用 。也许是这样的:LocalDate.now()
class Person {
final String name;
private final LocalDate dateOfBirth;
private final LocalDate currentDate;
// Used by regular production code
Person(String name, LocalDate dateOfBirth) {
this(name, dateOfBirth, LocalDate.now());
}
// Visible for test
Person(String name, LocalDate dateOfBirth, LocalDate currentDate) {
this.name = name;
this.dateOfBirth = dateOfBirth;
this.currentDate = currentDate;
}
long getAge() {
return ChronoUnit.YEARS.between(dateOfBirth, currentDate);
}
}
考虑一个场景,其中此人的生日自对象创建以来已过。使用此实现,将基于 Person 对象的创建时间,而不是当前日期。我们可以通过使用:getAge()
Supplier<LocalDate>
class Person {
final String name;
private final LocalDate dateOfBirth;
private final Supplier<LocalDate> currentDate;
// Used by regular production code
Person(String name, LocalDate dateOfBirth) {
this(name, dateOfBirth, ()-> LocalDate.now());
}
// Visible for test
Person(String name, LocalDate dateOfBirth, Supplier<LocalDate> currentDate) {
this.name = name;
this.dateOfBirth = dateOfBirth;
this.currentDate = currentDate;
}
long getAge() {
return ChronoUnit.YEARS.between(dateOfBirth, currentDate.get());
}
public static void main(String... args) throws InterruptedException {
// current date 2016-02-11
Person person = new Person("John Doe", LocalDate.parse("2010-02-12"));
printAge(person);
TimeUnit.DAYS.sleep(1);
printAge(person);
}
private static void printAge(Person person) {
System.out.println(person.name + " is " + person.getAge());
}
}
输出将正确为:
John Doe is 5
John Doe is 6
我们的单元测试可以像这样注入“now”日期:
@Test
void testGetAge() {
Supplier<LocalDate> injectedNow = ()-> LocalDate.parse("2016-12-01");
Person person = new Person("John Doe", LocalDate.parse("2004-12-01"), injectedNow);
assertEquals(12, person.getAge());
}