为什么这个与 Joda-Time 相关的测试有时会失败?

Why does this Joda-Time related test fail sometimes?

我有一个 class,我在其中计算当前日期 (now) 和其他日期 (birthday) 之间的年差:

import java.util.Date;

import org.joda.time.LocalDate;
import org.joda.time.Years;

public class Logic {
    private final Date now;

    public Logic(final Date curDate) {
        now = curDate;
    }

    [...]

    protected int getAgeInYears(final Date now, final Date birthday) {
        final LocalDate nowDate = new LocalDate(now.getTime());
        final LocalDate birthdayDate = new LocalDate(birthday.getTime());
        return Years.yearsBetween(birthdayDate, nowDate).getYears();
    }
}

然后我有一个单元测试,验证 getAgeInYears:

public class SomeTest {
    @Test
    public void test() {
        final LocalDate now = new LocalDate(2015, 8, 28);
        final Logic objectUnderTest =
            new Logic(now.toDate());
        final LocalDate birthDay1 = new LocalDate(2015 - 7, 8, 28);
        Assert.assertEquals(6,
            objectUnderTest.getAgeInYears(now.toDate(), birthDay1.toDate())); // This assertion fails sometimes, but not always
        final LocalDate birthDay2 = new LocalDate(2015 - 7, 8, 27);
        Assert.assertEquals(7,
            objectUnderTest.getAgeInYears(now.toDate(), birthDay2.toDate()));
        final LocalDate birthDay3 = new LocalDate(1981, 4, 24);
        Assert.assertEquals(34,
            objectUnderTest.getAgeInYears(now.toDate(), birthDay3.toDate()));

    }
}

断言

Assert.assertEquals(6, 
    objectUnderTest.getAgeInYears(now.toDate(), birthDay1.toDate()));

有时会失败。

在我的机器上测试运行没有错误。在测试失败的构建服务器之一 (Java 6) 上。当我使用 Java 6 在本地重建代码时,测试没有失败。

什么可能导致这种奇怪的行为?

更新一:错误信息为expected:<6> but was:<7>.

更新 2: 我重写了代码以摆脱 toDate 调用并得到了这个:

public class Logic {
    private final LocalDate now;

    public Logic(final LocalDate curDate) {
        now = curDate;
    }

    [...]

    protected int getAgeInYears(final LocalDate now, final LocalDate birthday) {
        final LocalDate nowDate = new LocalDate(now);
        final LocalDate birthdayDate = new LocalDate(birthday);
        return Years.yearsBetween(birthdayDate, nowDate).getYears();
    }
}

public class LogicTest {
    @Test
    public void getAgeInYearsSunnyDay() {
        final LocalDate now = new LocalDate(2015, 8, 28);
        final AdditionalServicesLogic objectUnderTest =
            new AdditionalServicesLogic(now);
        final LocalDate birthDay1 = new LocalDate(2015 - 7, 8, 28);
        Assert.assertEquals(7,
            objectUnderTest.getAgeInYears(now, birthDay1));
        final LocalDate birthDay2 = new LocalDate(2015 - 7, 8, 27);
        Assert.assertEquals(6,
            objectUnderTest.getAgeInYears(now, birthDay2));
        final LocalDate birthDay3 = new LocalDate(1981, 4, 24);
        Assert.assertEquals(34,
            objectUnderTest.getAgeInYears(now, birthDay3));

    }
}

现在我经常在网上收到断言错误

Assert.assertEquals(6,
    objectUnderTest.getAgeInYears(now, birthDay2));

我希望得到 6 个,但得到了 7 个。

您应该从 Logic class 中删除 java.util.Date 并且只在此处使用 joda classes。这将使您的代码更加清晰,因为不需要在 LocalDateDate 之间进行转换。实际上这样做会使您的测试变得毫无意义,因为您只会测试 Years.yearsBetween 的 joda 时间,我希望它已经被测试过。

我怀疑该错误可能与您的 LocalDate/Date 转换有关,您应该通过将 now.toDate()birthDay1.toDate() 的实际值打印到测试日志。并查看 LocalDate.toDate:

的 javadoc

Converting to a JDK Date is full of complications as the JDK Date constructor doesn't behave as you might expect around DST transitions. This method works by taking a first guess and then adjusting the JDK date until it has the earliest valid instant. This also handles the situation where the JDK time zone data differs from the Joda-Time time zone data.

Joda-Time 是正确的 请牢记您的代码:

您已将 birthDay2 明确设置为 2008-08-27。而您的 now-variable 是日期 2015-08-28。因此,如果您将 birthDay2 加 7 年,您将得到日期 2015-08-27,它仍然比 2015-08-28 早一天。

如果你有now=2015-08-27(那么正好相差7年),7年的结果不会改变。

6 年的预期只有在您将 birthDay2 设置为 2008-08-28 和现在=2015-08-27 时才正确(因为这里的日期比较会阻止完整的 7 -年增量)。

小旁注:

关于闰日有一个特殊情况需要注意(这里我认为 Joda-Time 是错误的)- 另请参阅此关闭 issue