Compare Date objects with different levels of precision
我有一个JUnit测试失败,因为毫秒是不同的。 在这种情况下,我不关心毫秒。 如何更改断言的精度以忽略毫秒(或我想要设置的任何精度)?
我想传递的失败断言的示例:
1 2 3 4 5 |
还有另一种解决方法,我会这样做:
1 | assertTrue("Dates aren't close enough to each other!", (date2.getTime() - date1.getTime()) < 1000); |
有些库可以帮助解决这个问题:
Apache commons-lang
如果在类路径上有Apache commons-lang,则可以使用
1 2 |
有一个简写:
1 |
请注意,12:00:00.001和11:59:00.999会截断为不同的值,因此这可能不太理想。为此,有圆:
AssertJ
从版本3.7.0开始,如果您使用的是Java 8 Date / Time API,AssertJ会添加一个
1 2 3 4 | LocalTime _07_10 = LocalTime.of(7, 10); LocalTime _07_42 = LocalTime.of(7, 42); assertThat(_07_10).isCloseTo(_07_42, within(1, ChronoUnit.HOURS)); assertThat(_07_10).isCloseTo(_07_42, within(32, ChronoUnit.MINUTES)); |
它也适用于遗留的Java日期:
1 2 3 |
使用
你可以这样做:
1 | assertTrue((date1.getTime()/1000) == (date2.getTime()/1000)); |
不需要字符串比较。
在JUnit中,您可以编写两个断言方法,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public class MyTest { @Test public void test() { ... assertEqualDates(expectedDateObject, resultDate); // somewhat more confortable: assertEqualDates("01/01/2012", anotherResultDate); } private static final String DATE_PATTERN ="dd/MM/yyyy"; private static void assertEqualDates(String expected, Date value) { DateFormat formatter = new SimpleDateFormat(DATE_PATTERN); String strValue = formatter.format(value); assertEquals(expected, strValue); } private static void assertEqualDates(Date expected, Date value) { DateFormat formatter = new SimpleDateFormat(DATE_PATTERN); String strExpected = formatter.format(expected); String strValue = formatter.format(value); assertEquals(strExpected, strValue); } } |
我不知道JUnit中是否有支持,但有一种方法可以做到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import java.text.SimpleDateFormat; import java.util.Date; public class Example { private static SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss"); private static boolean assertEqualDates(Date date1, Date date2) { String d1 = formatter.format(date1); String d2 = formatter.format(date2); return d1.equals(d2); } public static void main(String[] args) { Date date1 = new Date(); Date date2 = new Date(); if (assertEqualDates(date1,date2)) { System.out.println("true!"); } } } |
这实际上是一个比它看起来更难的问题,因为边界情况下你不关心的方差超过你正在检查的值的阈值。例如毫秒差异小于一秒,但两个时间戳跨越第二个阈值,或分钟阈值或小时阈值。这使得任何DateFormat方法本身都容易出错。
相反,我建议比较实际的毫秒时间戳,并提供一个方差增量,表示您认为两个日期对象之间可接受的差异。一个过于冗长的例子如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public static void assertDateSimilar(Date expected, Date actual, long allowableVariance) { long variance = Math.abs(allowableVariance); long millis = expected.getTime(); long lowerBound = millis - allowableVariance; long upperBound = millis + allowableVariance; DateFormat df = DateFormat.getDateTimeInstance(); boolean within = lowerBound <= actual.getTime() && actual.getTime() <= upperBound; assertTrue(MessageFormat.format("Expected {0} with variance of {1} but received {2}", df.format(expected), allowableVariance, df.format(actual)), within); } |
为Joda-Time使用AssertJ断言(http://joel-costigliola.github.io/assertj/assertj-joda-time.html)
1 2 3 4 | import static org.assertj.jodatime.api.Assertions.assertThat; import org.joda.time.DateTime; assertThat(new DateTime(dateOne.getTime())).isEqualToIgnoringMillis(new DateTime(dateTwo.getTime())); |
测试失败消息更具可读性
1 2 3 4 5 6 | java.lang.AssertionError: Expecting: <2014-07-28T08:00:00.000+08:00> to have same year, month, day, hour, minute and second as: <2014-07-28T08:10:00.000+08:00> but had not. |
使用JUnit 4,您还可以实现匹配器,以根据您选择的精度测试日期。在此示例中,匹配器将字符串格式表达式作为参数。对于此示例,代码不短。但是匹配器类可以重用;如果你给它一个描述名称,你可以用优雅的方式记录测试的意图。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import static org.junit.Assert.assertThat; // further imports from org.junit. and org.hamcrest. @Test public void testAddEventsToBaby() { Date referenceDate = new Date(); // Do something.. Date testDate = new Date(); //assertThat(referenceDate, equalTo(testDate)); // Test on equal could fail; it is a race condition assertThat(referenceDate, sameCalendarDay(testDate,"yyyy MM dd")); } public static Matcher<Date> sameCalendarDay(final Object testValue, final String dateFormat){ final SimpleDateFormat formatter = new SimpleDateFormat(dateFormat); return new BaseMatcher<Date>() { protected Object theTestValue = testValue; public boolean matches(Object theExpected) { return formatter.format(theExpected).equals(formatter.format(theTestValue)); } public void describeTo(Description description) { description.appendText(theTestValue.toString()); } }; } |
如果您使用Joda,您可以使用Fest Joda Time。
JUnit有一个内置的断言,用于比较双精度数,并指定它们需要的接近程度。在这种情况下,delta是您认为日期等效的毫秒数。此解决方案没有边界条件,测量绝对方差,可以轻松指定精度,并且不需要编写额外的库或代码。
1 2 3 4 5 6 7 8 | Date dateOne = new Date(); dateOne.setTime(61202516585000L); Date dateTwo = new Date(); dateTwo.setTime(61202516585123L); // this line passes correctly Assert.assertEquals(dateOne.getTime(), dateTwo.getTime(), 500.0); // this line fails correctly Assert.assertEquals(dateOne.getTime(), dateTwo.getTime(), 100.0); |
注意它必须是100.0而不是100(或者需要加倍转换)以强制它将它们作为双精度进行比较。
只需比较您有兴趣比较的日期部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | Date dateOne = new Date(); dateOne.setTime(61202516585000L); Date dateTwo = new Date(); dateTwo.setTime(61202516585123L); assertEquals(dateOne.getMonth(), dateTwo.getMonth()); assertEquals(dateOne.getDate(), dateTwo.getDate()); assertEquals(dateOne.getYear(), dateTwo.getYear()); // alternative to testing with deprecated methods in Date class Calendar calOne = Calendar.getInstance(); Calendar calTwo = Calendar.getInstance(); calOne.setTime(dateOne); calTwo.setTime(dateTwo); assertEquals(calOne.get(Calendar.MONTH), calTwo.get(Calendar.MONTH)); assertEquals(calOne.get(Calendar.DATE), calTwo.get(Calendar.DATE)); assertEquals(calOne.get(Calendar.YEAR), calTwo.get(Calendar.YEAR)); |
这是一个实用功能,为我完成了这项工作。
像这样的东西可能会起作用:
1 2 | assertEquals(new SimpleDateFormat("dd MMM yyyy").format(dateOne), new SimpleDateFormat("dd MMM yyyy").format(dateTwo)); |
您可以创建一个小型协作者,而不是直接使用
创建一个DateBuilder成员并将调用从
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import java.util.Date; public class Demo { DateBuilder dateBuilder = new DateBuilder(); public void run() throws InterruptedException { Date dateOne = dateBuilder.now(); Thread.sleep(10); Date dateTwo = dateBuilder.now(); System.out.println("Dates are the same:" + dateOne.equals(dateTwo)); } public static void main(String[] args) throws InterruptedException { new Demo().run(); } } |
主要方法将产生:
1 | Dates are the same: false |
在测试中,您可以注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class DemoTest { @org.junit.Test public void testMockito() throws Exception { DateBuilder stub = org.mockito.Mockito.mock(DateBuilder.class); org.mockito.Mockito.when(stub.now()).thenReturn(new java.util.Date(42)); Demo demo = new Demo(); demo.dateBuilder = stub; demo.run(); } @org.junit.Test public void testAnonymousClass() throws Exception { Demo demo = new Demo(); demo.dateBuilder = new DateBuilder() { @Override public Date now() { return new Date(42); } }; demo.run(); } } |
使用SimpleDateFromat将日期转换为String,在构造函数中指定所需的日期/时间字段并比较字符串值:
1 2 3 4 | SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String expectedDate = formatter.format(dateOne)); String dateToTest = formatter.format(dateTwo); assertEquals(expectedDate, dateToTest); |
我做了一个小课程,可能对一些最终在这里的谷歌有用:https://stackoverflow.com/a/37168645/5930242
您可以在比较日期时选择所需的精度等级,例如:
1 2 3 | LocalDateTime now = LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS); // e.g. in MySQL db"timestamp" is without fractional seconds precision (just up to seconds precision) assertEquals(myTimestamp, now); |
我将对象转换为java.util.Date并进行比较