정밀도 수준이 다른 Date 객체 비교
밀리 초가 다르기 때문에 실패한 JUnit 테스트가 있습니다. 이 경우 밀리 초는 신경 쓰지 않습니다. 밀리 초 (또는 설정하려는 정밀도)를 무시하도록 어설 션의 정밀도를 어떻게 변경할 수 있습니까?
통과하고 싶다고 주장하는 실패의 예 :
Date dateOne = new Date();
dateOne.setTime(61202516585000L);
Date dateTwo = new Date();
dateTwo.setTime(61202516585123L);
assertEquals(dateOne, dateTwo);
DateFormat
일치하려는 부분 만 표시하는 형식 의 개체를 사용 assertEquals()
하고 결과 문자열에 대해 수행합니다 . 자신의 assertDatesAlmostEqual()
방법으로 쉽게 감쌀 수도 있습니다 .
또 다른 해결 방법은 다음과 같습니다.
assertTrue("Dates aren't close enough to each other!", (date2.getTime() - date1.getTime()) < 1000);
이를 도와주는 라이브러리가 있습니다.
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));
AssertJ
버전 3.7.0부터 Java 8 날짜 / 시간 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));
레거시 자바 날짜에서도 작동합니다.
Date d1 = new Date();
Date d2 = new Date();
assertThat(d1).isCloseTo(d2, within(100, ChronoUnit.MILLIS).getValue());
다음과 같이 할 수 있습니다.
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!"); }
}
}
이것은 당신이 신경 쓰지 않는 분산이 당신이 체크하고있는 값에 대한 임계 값을 넘는 경계 케이스 때문에 나타나는 것보다 실제로 더 어려운 문제입니다. 예를 들어 밀리 초 차이가 1 초 미만이지만 두 타임 스탬프가 두 번째 임계 값, 분 임계 값 또는 시간 임계 값을 교차합니다. 따라서 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는 문자열 형식 표현식을 매개 변수로 사용합니다. 이 예제에서는 코드가 더 짧지 않습니다. 그러나 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.
Joda를 사용하는 경우 Fest Joda Time을 사용할 수 있습니다 .
비교하려는 날짜 부분을 비교하십시오.
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 has a built in assertion for comparing doubles, and specifying how close they need to be. In this case, the delta is within how many milliseconds you consider dates equivalent. This solution has no boundary conditions, measures absolute variance, can easily specify precision, and requires no additional libraries or code to be written.
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);
Note It must be 100.0 instead of 100 (or a cast to double is needed) to force it to compare them as doubles.
Something like this might work:
assertEquals(new SimpleDateFormat("dd MMM yyyy").format(dateOne),
new SimpleDateFormat("dd MMM yyyy").format(dateTwo));
Instead of using new Date
directly, you can create a small collaborator, which you can mock out in your test:
public class DateBuilder {
public java.util.Date now() {
return new java.util.Date();
}
}
Create a DateBuilder member and change calls from new Date
to 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();
}
}
The main method will produce:
Dates are the same: false
In the test you can inject a stub of DateBuilder
and let it return any value you like. For example with Mockito or an anonymous class which overrides 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();
}
}
Convert the dates to String using SimpleDateFromat, specify in the constructor the required date/time fields and compare the string values:
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String expectedDate = formatter.format(dateOne));
String dateToTest = formatter.format(dateTwo);
assertEquals(expectedDate, dateToTest);
I did a small class that might be useful for some googlers that end up here : https://stackoverflow.com/a/37168645/5930242
You can chose which precision level you want when comparing dates, e.g.:
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);
Here is a utility function that did the job for me.
private boolean isEqual(Date d1, Date d2){
return d1.toLocalDate().equals(d2.toLocalDate());
}
i cast the objects to java.util.Date and compare
assertEquals((Date)timestamp1,(Date)timestamp2);
참고URL : https://stackoverflow.com/questions/1671001/compare-date-objects-with-different-levels-of-precision
'Programing' 카테고리의 다른 글
Type의 기본 생성자를 얻는 가장 효율적인 방법 (0) | 2020.11.04 |
---|---|
WPF 명령 새로 고침 (0) | 2020.11.04 |
SQL * Plus를 사용하여 Oracle 11g에서 데이터베이스를 표시하는 방법 (0) | 2020.11.04 |
Maven에서 카테고리별로 JUnit 테스트를 실행하는 방법은 무엇입니까? (0) | 2020.11.04 |
C ++ 크로스 플랫폼 고해상도 타이머 (0) | 2020.11.03 |