我有一个JUnit测试失败,因为毫秒不同。在这种情况下,我不在乎毫秒。如何更改断言的精度以忽略毫秒(或我希望将其设置为任何精度)?
我想通过的断言示例:
Date dateOne = new Date();
dateOne.setTime(61202516585000L);
Date dateTwo = new Date();
dateTwo.setTime(61202516585123L);
assertEquals(dateOne, dateTwo);
Answers:
使用具有DateFormat
仅显示您要匹配的部分的格式的对象,assertEquals()
并对所得的字符串进行处理。您还可以轻松地将其包装在自己的assertDatesAlmostEqual()
方法中。
另一个解决方法是,我会这样:
assertTrue("Dates aren't close enough to each other!", (date2.getTime() - date1.getTime()) < 1000);
有一些库可以帮助您:
Apache Commons语言
如果您的类路径上有Apache commons-lang,则可以用于DateUtils.truncate
将日期截断到某个字段。
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));
断言
从3.7.0版开始,如果您使用的是Java 8 Date / Time API,则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));
它也适用于旧版Java Date:
Date d1 = new Date();
Date d2 = new Date();
assertThat(d1).isCloseTo(d2, within(100, ChronoUnit.MILLIS).getValue());
java.sql.Timestamps
并且DateUtils.truncate(...)
在Java 8中为我工作。我的特殊情况包括一种数据库技术,该技术不支持保存小于一秒的细粒度,因此我将内存中的时间戳与已保存的时间戳进行了比较。从数据库检索。内存中的时间戳比从数据库读取的时间戳具有更高的精度。
您可以执行以下操作:
assertTrue((date1.getTime()/1000) == (date2.getTime()/1000));
不需要字符串比较。
在JUnit中,您可以编写两个assert方法,如下所示:
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是否提供支持,但是有一种方法可以做到:
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!"); }
}
}
assertEqualDates
那么我将使其返回类型void
并进入最后一行assertEquals(d1, d2)
。这样,它的行为将与所有JUnitassert*
方法相同。
实际上,这是一个比表面上更棘手的问题,因为在边界情况下,您不关心的方差超过了要检查的值的阈值。例如,毫秒差小于一秒,但两个时间戳超过第二阈值或分钟阈值或小时阈值。这使得任何DateFormat方法本质上都容易出错。
相反,我建议比较实际的毫秒时间戳,并提供方差增量,以表明您认为两个日期对象之间的可接受差异。一个过于冗长的示例如下:
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,您还可以根据您选择的精度实现一个匹配器来测试日期。在此示例中,匹配器将字符串格式表达式作为参数。该示例的代码并不短。但是matcher类可以重用;如果给它起了描述性的名称,则可以用一种优雅的方式在测试中记录意图。
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()));
测试失败的消息更具可读性
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.
assertThat(new Date(2016 - 1900, 0, 1,12,13,14)).isEqualToIgnoringMillis("2016-01-01T12:13:14");
只需比较您要比较的日期部分:
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有一个内置的断言,用于比较双精度数,并指定它们需要多近。在这种情况下,变化量在您认为等效日期的毫秒数内。该解决方案没有边界条件,可以测量绝对方差,可以轻松指定精度,并且不需要编写其他库或代码。
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),以强制将它们比较为double。
您可以在比较日期时选择所需的精度级别,例如:
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);
这样的事情可能会起作用:
assertEquals(new SimpleDateFormat("dd MMM yyyy").format(dateOne),
new SimpleDateFormat("dd MMM yyyy").format(dateTwo));
new Date
您可以创建一个小型协作者,而不是直接使用它,您可以在测试中模拟它:
public class DateBuilder {
public java.util.Date now() {
return new java.util.Date();
}
}
创建一个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
在测试中,您可以插入的存根,DateBuilder
并使其返回您喜欢的任何值。例如,使用Mockito或重写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();
}
}
我将对象转换为java.util.Date并进行比较
assertEquals((Date)timestamp1,(Date)timestamp2);