比较具有不同精度级别的Date对象
我有一个JUnittesting失败,因为毫秒是不同的。 在这种情况下,我不在乎毫秒。 我怎样才能改变断言的精度忽略毫秒(或任何精度,我想它设置为)?
我想通过一个失败的断言示例:
Date dateOne = new Date(); dateOne.setTime(61202516585000L); Date dateTwo = new Date(); dateTwo.setTime(61202516585123L); assertEquals(dateOne, dateTwo);
使用DateFormat
对象,其格式只显示要匹配的部分,并在生成的Strings上执行assertEquals()
。 您也可以轻松地将其包装在您自己的assertDatesAlmostEqual()
方法中。
又一个解决方法,我会这样做:
assertTrue("Dates aren't close enough to each other!", (date2.getTime() - date1.getTime()) < 1000);
有这样的图书馆帮助:
阿帕奇公用语言
如果您的类path中包含Apache commons-lang ,则可以使用DateUtils.truncate
将date截断为某个字段。
assertEquals(DateUtils.truncate(date1,Calendar.SECOND), DateUtils.truncate(date2,Calendar.SECOND));
有一个这样的简写:
assertTrue(DateUtils.truncatedEquals(date1,date2,Calendar.SECOND));
请注意,12:00:00.001和11:59:00.999会截断为不同的值,因此这可能不理想。 为此,有一轮:
assertEquals(DateUtils.round(date1,Calendar.SECOND), DateUtils.round(date2,Calendar.SECOND));
AssertJ
如果使用Java 8 Date / Time API, 则从 3.7.0版开始, AssertJ添加了isCloseTo
断言。
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));
它也适用于传统的Javadate:
Date d1 = new Date(); Date d2 = new Date(); assertThat(d1).isCloseTo(d2, within(100, ChronoUnit.MILLIS).getValue());
你可以做这样的事情:
assertTrue((date1.getTime()/1000) == (date2.getTime()/1000));
没有string比较需要。
我不知道在JUnit中是否有支持,但是有一种方法:
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!"); } } }
在JUnit中,您可以编写两个断言方法,如下所示:
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); } }
这实际上是一个比看起来更难的问题,因为您不关心的变化跨越了您正在检查的值的阈值的边界情况。 例如,毫秒差异小于一秒,但是两个时间戳与第二阈值或分钟阈值或小时阈值相交。 这使得任何DateFormat方法本身就很容易出错。
相反,我会build议比较实际毫秒时间戳,并提供方差增量,指出您认为两个date对象之间的可接受差异。 一个非常详细的例子如下:
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); }
使用JUnit 4,你也可以根据你select的精度实现一个testingdate的匹配器 。 在这个例子中,匹配器将一个string格式expression式作为参数。 这个例子中的代码并没有缩短。 然而,匹配器类可能会被重用。 如果你给它一个描述性的名字,你可以通过一个优雅的方式来logging这个testing的意图。
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-Time使用AssertJ声明( http://joel-costigliola.github.io/assertj/assertj-joda-time.html )
import static org.assertj.jodatime.api.Assertions.assertThat; import org.joda.time.DateTime; assertThat(new DateTime(dateOne.getTime())).isEqualToIgnoringMillis(new DateTime(dateTwo.getTime()));
testing失败消息更具可读性
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.
如果你使用乔达,你可以使用费斯特乔达时间 。
只需比较你感兴趣比较的date部分:
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));
JUnit有一个内置的用于比较double的断言,并指定它们需要多么接近。 在这种情况下,增量在你认为相当于几个毫秒的时间内。 该解决scheme没有边界条件,测量绝对差异,可以很容易地指定精度,并且不需要额外的库或代码来编写。
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(或者需要强制转换为double)来强制它将它们比作双打。
像这样的东西可能会工作:
assertEquals(new SimpleDateFormat("dd MMM yyyy").format(dateOne), new SimpleDateFormat("dd MMM yyyy").format(dateTwo));
不要直接使用new Date
,你可以创build一个小型的合作者,你可以在你的testing中嘲笑:
public class DateBuilder { public java.util.Date now() { return new java.util.Date(); } }
创build一个DateBuilder成员,并将new Date
调用更改为dateBuilder.now()
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(); } }
主要方法将产生:
Dates are the same: false
在testing中,你可以注入一个DateBuilder
的存根,并让它返回你喜欢的任何值。 例如,Mockito或now()
覆盖now()
的匿名类:
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将date转换为String,在构造函数中指定所需的date/时间字段并比较string值:
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
我将对象转换为java.util.Date并进行比较
assertEquals((Date)timestamp1,(Date)timestamp2);