我有一个带有1种方法的Util类,它使用joda-time来计算年龄:
public static int getAge(LocalDate birthdate) {
LocalDate today = new LocalDate();
Period period = new Period(birthdate, today, PeriodType.yearMonthDay());
return period.getYears();
}
我已经编写了一个广泛的JUnit测试类但是如何使它与时间无关?如果我创建一个测试用例来计算某人的年龄,如果他们今天出生于1970年,那么结果将是46岁。但是从现在起1年后,如果我运行测试用例,它将失败,因为结果将是47。
那么如何使这些测试用例与时间无关。我正在考虑使用某种日历界面,测试用例将从中创建日期对象。我也偶然发现了this帖子,这是另一种可能的解决办法,但我不确定该怎么做。
答案 0 :(得分:4)
一种常见做法(在测试指导下的面向成长面向对象的软件一书中描述)是将创建日期实例的责任委托给协作者。
想象一下,你有一个这样的课:
function copyBoth() {
var sh = SpreadsheetApp.getActiveSheet();
var dataLngth = sh.getLastRow();
var rngCol_D = sh.getRange(2, 4, dataLngth, 1); //Set range for source - Example is column D
var formulas = rngCol_D.getFormulas(); //Get all the forumlas from the source range
var values = rngCol_D.getValues();
var rngCol_A = sh.getRange(2, 1, values.length, 1); //Set range for destination - Example is column A
rngCol_A.setFormulas(formulas);//Write all the formulas to the destination:
//Write all the values, individually to each cell.
var i = 0, thisFormula;
for (i=0;i<dataLngth;i+=1) {
thisFormula = formulas[i][0];
//Logger.log('thisFormula: ' + thisFormula);
//Logger.log('typeof thisFormula: ' + typeof thisFormula);
if (thisFormula === "") {
sh.getRange(i+2, 1).setValue(values[i][0]);//Write the individual value to the single cell
};
};
};
那么您的代码可能会变成
public class Clock {
public LocaleDate now(){
return new LocalDate();
}
}
我可以不测试public static int getAge(LocalDate birthdate, Clock clock) {
LocalDate today = clock.now();
Period period = new Period(birthdate, today, PeriodType.yearMonthDay());
return period.getYears();
}
,因为它很简单,但我现在可以传入一个模拟的时钟实例来测试Clock#now
方法。
现在这意味着使用静态getAge
的类必须提供时钟,时钟将是此类的依赖项。可以以各种可模拟的方式注入这种依赖性。
我个人属于getAge
学校,但是从injection by constructor
到proctected工厂方法(因此可以在用于创建测试实例的匿名子类中重载)的任何工作都有效。
这个问题和我建议的设计会在the book,很多blog posts和stack overflow中进行详细讨论(如果java 8看起来像direct support那么你愿意转向java.time)
答案 1 :(得分:2)
使用固定日期:
LocalDate today = new LocalDate(2016, 1, 5);
答案 2 :(得分:1)
所以,首先让我说我同意Jean
。但是我发现这种模式很常见,我在GitHub上的库中实现了一个可重用的解决方案,名为LibEx。它被称为DateSupplier
。要获取当前DateTime
,请使用DateSupplier.getCurrentDateTime()
。然后在测试中使用允许@Rule DateController
的{{1}}。在测试中设置此项时,datController.setCurrentDateTo...()
将返回DateSupplier
指定的值。
以下是链接:
您的测试将会是这样的..
DateController
由于public class DateControllerTest extends TestBase {
@Rule
public DateController dateContoller = new DateController();
@Test
public void testSetCurrentTime() {
// setup
Date date = new Date(123443);
// test
dateContoller.setCurrentTime(date);
// verify
assertDateEquals(date);
}
private void assertDateEquals(Date date) {
assertThat(DateSupplier.getCurrentDate(), equalTo(date));
assertThat(DateSupplier.getCurrentDateInMilliseconds(), equalTo(date.getTime()));
}
是DateController
,因此每次测试后都会重置为默认行为。此外,Rule
位于DateController
模块中,应该是测试范围,因此它已部署在您的生产代码中。
答案 3 :(得分:-1)
所以你想要今天46岁的人的出生日期? (你的问题不明确。)
Joda-Time的制造商告诉我们尽快使用Java 8及更高版本中内置的java.time框架。
ZonedDateTime birthdateOf46YearOld = ZonedDateTime.now().minusYears( 46 );
此对象隐式分配了JVM的当前默认时区。我建议始终明确你的时区。
ZoneId zoneId = ZoneId.of( "America/Montreal" ); // Or… ZoneOffset.UTC …or… ZoneId.systemDefault().
ZonedDateTime birthdateOf46YearOld = ZonedDateTime.now( zoneId ).minusYears( 46 );
或者您想测试手头的日期时间值是否与出生日期相同?
Boolean hit = someZdt.isEqual( birthdateOf46YearOld );
如果您只关心没有时间或时区的日期,那么请使用{Jame-Time类似的LocalDate
。请注意,时区对于确定今天的日期至关重要,因为它在全球各个时区之间会有所不同。
LocalDate birthdate = LocalDate.now( zoneId ).minusYears( 46) ;
如果您只想要一对LocalDate对象之间的经过时间,请使用Period
类。
LocalDate today = LocalDate.now( zoneId );
LocalDate birthDate = … ;
Period period = Period.between( birthDate , today );
Integer ageInYears = period.getYears();
Boolean hit = ageInYears.equals ( Integer.valueOf( 46 ) ) ;
如果您无法使用Java 8或更高版本,则相同类型的代码可在Joda-Time中使用。
DateTimeZone zone = DateTimeZone.forID( "America/Montreal" );
DateTime birthdate = DateTime.now( zone ).minusYears( 46 );