与时间无关的JUnit测试用例

时间:2016-01-05 19:19:53

标签: java unit-testing date junit jodatime

我有一个带有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帖子,这是另一种可能的解决办法,但我不确定该怎么做。

4 个答案:

答案 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 postsstack 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岁的人的出生日期? (你的问题不明确。)

java.time

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 );