如何测试应返回特定日期的日期函数

时间:2016-11-17 20:59:42

标签: java date testing

我有以下功能:

public static Date getFirstOfLastMonth() {
    Calendar cal = Calendar.getInstance();
    cal.add(Calendar.MONTH, -1);
    cal.set(Calendar.DAY_OF_MONTH, 1);

    return cal.getTime();
}

如何编写单元测试以检查此函数返回的值是否与预期值相同而不使用相同的逻辑来生成预期值?

4 个答案:

答案 0 :(得分:3)

有关使用JodaTime或Java 8的建议,请参阅其他答案。但是,这可以使用java.util.Calendar来完成。

关键是要改变你的方法以传递日期,而不是让它假设当前时间。也许考虑重命名此方法以反映其新语义,请参阅Andreas对getFirstOfPreviousMonth的建议。

您需要将其称为getFirstOfLastMonth(new Date())以保留现有行为,甚至可能使用默认方法

public static Date getFirstOfLastMonth() {
     return getFirstOfPreviousMonth(new Date());
}

public static Date getFirstOfPreviousMonth(Date now) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(now);
    cal.add(Calendar.MONTH, -1);
    cal.set(Calendar.DAY_OF_MONTH, 1);

    return cal.getTime();
}

然后进行一些测试,您可以使用Calendar来检查结果:

@Test
public void previousYear() {
    Calendar input = Calendar.getInstance();
    input.clear();
    input.set(2009, Calendar.JANUARY, 5);

    Date result = getFirstOfLastMonth(input.getTime());

    Calendar output = Calendar.getInstance();
    output.setTime(result);
    assertThat(output.get(Calendar.YEAR), is(2008));
    assertThat(output.get(Calendar.MONTH), is(Calendar.DECEMBER));
    assertThat(output.get(Calendar.DAY_OF_MONTH), is(1));
}

答案 1 :(得分:1)

添加需要传递时间戳的方法:

static Date getFirstOfPreviousMonth(long timeInMillis)
{
    Calendar cal = Calendar.getInstance();
    cal.setTimeInMillis( timeInMillis );

    cal.add(Calendar.MONTH, -1);
    cal.set(Calendar.DAY_OF_MONTH, 1);

    return cal.getTime();
}

现在,您可以通过传递特定时刻来测试此方法。

原始方法,通过使用当前系统时间调用此包私有方法来实现它。

public static Date getFirstOfLastMonth() {
    getFirstOfPreviousMonth(System.currentTimeMillis());
}

答案 2 :(得分:1)

Answer by Adam是正确的,应该被接受。

它提到你应该(真的应该是)使用现代java.time类而不是麻烦的旧遗留日期时间类。这是适用于java.time类的Adam代码的一个版本。

此外,时区对于确定像月初的日期至关重要。对于任何给定的时刻,日期在全球范围内因地区而异。例如,新西兰奥克兰的新一天早于蒙特利尔魁北克。如果省略,则JVM会隐式应用其当前的默认时区。在运行时期间,该默认值可随时更改。最好明确指定。

Instant

Instant类代表UTC中时间轴上的一个时刻,分辨率为nanoseconds(小数部分最多九(9)位)。

ZonedDateTime

这个Instant类是java.time的基本构建块类。您可以将OffsetDateTime视为Instant加上ZoneOffset。同样,您可以将ZonedDateTime视为Instant加上ZoneId

如果您只关心日期而不关心时间,请使用LocalDate课程。

TemporalAdjuster

TemporalAdjuster接口定义了用于操作日期时间值的类,例如获取第一个月。 TemporalAdjusters类(注意复数'')提供firstDayOfMonth等实现。

public static ZonedDateTime getFirstOfLastMonth( ZoneId z ) {
    Instant instant = Instant.now();
    return getFirstOfPreviousMonth( instant , z );
}

public static ZonedDateTime getFirstOfPreviousMonth( Instant instantArg , ZoneId zoneArg ) {
    ZonedDateTime zdt = instantArg.atZone( zoneArg);
    ZonedDateTime firstOfMonthZdt = zdt.with( TemporalAdjusters.firstDayOfMonth() );
    ZonedDateTime firstOfMonthPriorZdt = firstOfMonthZdt.minusMonths( 1 );

    return firstOfMonthPriorZdt;
}

Clock

您可以传递一个特殊的Clock实现进行测试,而不是默认使用实时时钟实现。该时钟提供静态方法来生成几个这样的替代实现。

例如,fixed实施始终提供相同的frozen moment in time。假设您希望时钟始终在Québec时间报告2017年3月21日的第一个时刻。

LocalDate ld = LocalDate.of( 2017 , Month.MARCH , 21 );
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ld.atStartOfDay( z ); // Let java.time determine first moment of the day. Not always `00:00:00`.
Instant instant = zdt.toInstant();
Clock c = Clock.fixed( instant , z );
// Pass this clock `c` in your calls to various java.time methods.

关于java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和& SimpleDateFormat

现在位于Joda-Timemaintenance mode项目建议迁移到java.time。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore

答案 3 :(得分:-1)

使用Joda或Java 8库。 Java< = 7以这种方式非常不足。您需要做的是在Calendar下面并嘲笑它认为日期是某个固定的日期,如2010年5月31日或任何其他日期。然后您的单元测试使用硬编码日期。

使用Calendar执行此操作有点啰嗦。

  1. 您的方法不能是static
  2. 您的类构造函数将使用返回日历的CalendarFactory并调用factory.get()而不是Calendar.getInstance()
  3. 在测试代码中,您将注入一个CalendarFactory,用于输出已在其上调用.set(someFixedDate)的日历。
  4. 然后,您将针对固定日期进行单元测试。
  5. 不管你信不信,这实际上是OO可测试代码的代码。在我看来,OO的最佳成就之一是让开发人员表达可测试的代码。一个静态的方法不会在这里切割,抱歉。