Подтвердить что ты не робот

Сравнить объекты даты с различными уровнями точности

У меня есть тест JUnit, который терпит неудачу, потому что миллисекунды разные. В этом случае меня не волнует миллисекунды. Как я могу изменить точность assert для игнорирования миллисекунд (или любой точности, которую я хотел бы установить)?

Пример неудачного утверждения, которое я хотел бы передать:

Date dateOne = new Date();
dateOne.setTime(61202516585000L);
Date dateTwo = new Date();
dateTwo.setTime(61202516585123L);
assertEquals(dateOne, dateTwo);
4b9b3361

Ответ 1

Используйте объект DateFormat с форматом, который отображает только те части, которые вы хотите сопоставить, и выполните assertEquals() в результирующих строках, Вы также можете легко обернуть это в свой собственный метод assertDatesAlmostEqual().

Ответ 2

Еще одно обходное решение, я бы сделал это следующим образом:

assertTrue("Dates aren't close enough to each other!", (date2.getTime() - date1.getTime()) < 1000);

Ответ 3

Существуют библиотеки, которые помогают с этим:

Apache commons-lang

Если у вас есть 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, AssertJ добавил isCloseTo, если вы используете API-интерфейс Java 8 Date/Time.

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 d1 = new Date();
Date d2 = new Date();
assertThat(d1).isCloseTo(d2, within(100, ChronoUnit.MILLIS).getValue());

Ответ 4

Вы можете сделать что-то вроде этого:

assertTrue((date1.getTime()/1000) == (date2.getTime()/1000));

Нет необходимости в сравнении строк.

Ответ 5

В 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);
  }
}

Ответ 6

Я не знаю, есть ли поддержка в 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!"); }
    }
}

Ответ 7

На самом деле проблема сложнее, чем из-за граничных случаев, когда разница, которую вы не заботитесь, пересекает порог для значения, которое вы проверяете. например разница в миллисекундах меньше секунды, но две временные метки пересекают второй порог или минутный порог или часовой порог. Это делает любой подход 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);
}

Ответ 8

Используя 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());
        }
    };
}

Ответ 9

использовать утверждения AssertJ для Joda-Time (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.

Ответ 10

Просто сравните детали, которые вы хотите сравнить:

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));

Ответ 11

Если вы использовали Joda, вы можете использовать Fest Joda Time.

Ответ 12

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 (или требуется бросок, чтобы удвоить), чтобы заставить их сравнивать их как двойные.

Ответ 13

Что-то вроде этого может работать:

assertEquals(new SimpleDateFormat("dd MMM yyyy").format(dateOne),
                   new SimpleDateFormat("dd MMM yyyy").format(dateTwo));

Ответ 14

Вместо непосредственного использования 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();
    }
}

Ответ 15

Преобразуйте даты в String с помощью SimpleDateFromat, укажите в конструкторе необходимые поля даты и времени и сравните строковые значения:

SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String expectedDate = formatter.format(dateOne));
String dateToTest = formatter.format(dateTwo);
assertEquals(expectedDate, dateToTest);

Ответ 16

Я сделал небольшой класс, который может быть полезен для некоторых googlers, которые здесь: fooobar.com/questions/96663/...

Ответ 17

Вы можете выбрать уровень точности при сравнении дат, например:

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);

Ответ 18

Вот полезная функция, которая сделала эту работу за меня.

    private boolean isEqual(Date d1, Date d2){
        return d1.toLocalDate().equals(d2.toLocalDate());
    }

Ответ 19

я отбрасываю объекты в java.util.Date и сравниваю

assertEquals((Date)timestamp1,(Date)timestamp2);