2009-11-04 12 views
59

Mam test JUnit, który kończy się niepowodzeniem, ponieważ milisekundy są różne. W tym przypadku nie dbam o milisekundy. Jak mogę zmienić dokładność asserta, aby zignorować milisekundy (lub jakąkolwiek precyzję, którą chciałbym ustawić)?Porównaj Obiekty daty o różnych poziomach dokładności

Przykład dochodzić braku takiego chciałabym przekazać:

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

Odpowiedz

21

Użyj DateFormat obiekt w formacie, który pokazuje tylko te części, które chcesz dopasować i zrobić assertEquals() na wynikającym z pszczół. Możesz również łatwo zawijać to w swojej własnej metodzie assertDatesAlmostEqual().

+6

Nie obsługuje przypadku różnicy milisekundy nad drugą granicą, 10.000 i 09.999 byłyby inne. – scarba05

4

ja nie wiem, czy nie ma wsparcia w JUnit, ale jednym ze sposobów, aby to zrobić:

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!"); } 
    } 
} 
+0

Jeśli wywołasz metodę "assertEqualDates", wtedy wykonam jej typ zwracający 'void' i utworzę ostatni wiersz' assertEquals (d1, d2) '. W ten sposób zachowywałby się tak samo jak wszystkie metody JUnit 'assert *'. –

+0

Uzgodnione. Chciałem uruchomić kod i nie miałem pod ręką JUnit. –

+1

Uważaj na globalne formaty daty. Nie są bezpieczne dla wątków. To nie jest problem z tym kodem, ale jest to zły nawyk. – itsadok

6

Można zrobić coś takiego:

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

żadnych porównań String potrzebne.

+0

Myślę, że miałeś na myśli "/" versus "%"? Staje się to niechlujne w odniesieniu do arbitralnej precyzji, IMHO. Dobra uwaga. –

+0

Whoops! Dobry połów. Nie uważam jednak, że precyzja jest problemem. Date.getTime() zawsze zwraca długi czas od epoki. – Seth

+0

Nie powiedzie się, jeśli jedna wartość wynosi 3.999 sekundy, a druga 4.000. Innymi słowy, czasami toleruje różnicę do sekund, czasami nie uda się z różnicą 2 ms. –

44

Kolejny obejście, zrobiłbym to tak:

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

+1 dla porównania wariancji, ale nie uwzględnia bezwzględnej wariancji (np. Co jeśli data1 jest po dacie2?) – Ophidian

+9

Podejściem podobnym jest zwykle owijanie przy pomocy Math.abs() – parxier

40

Są to biblioteki, które w tym pomóc:

Apache Commons-lang

Jeśli masz Apache commons-lang na swoją ścieżkę klas, możesz użyć DateUtils.truncate, aby skrócić daty do niektórych pól.

assertEquals(DateUtils.truncate(date1,Calendar.SECOND), 
      DateUtils.truncate(date2,Calendar.SECOND)); 

Jest to skrót od:

assertTrue(DateUtils.truncatedEquals(date1,date2,Calendar.SECOND)); 

Należy zauważyć, że 12: 00: 00,001 i 11: 59: 00,999, że obcina się różne wartości, to może to nie jest idealny. Za to, że jest okrągła:

assertEquals(DateUtils.round(date1,Calendar.SECOND), 
      DateUtils.round(date2,Calendar.SECOND)); 

AssertJ

Począwszy od wersji 3.7.0, AssertJ Dodano isCloseTo twierdzeń, jeśli używasz Java 8 Data/godzina API.

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

Działa również ze starszymi java Daty także:

Date d1 = new Date(); 
Date d2 = new Date(); 
assertThat(d1).isCloseTo(d2, within(100, ChronoUnit.MILLIS).getValue()); 
+0

to rozwiązanie, którego szukałem :) – geoaxis

+0

Dzięki temu zaoszczędziłem mnóstwo czasu! –

+0

Dlaczego nie używać DateUtils.round? – domi

3

Właściwie jest to trudniejsze niż się wydaje problemem ze względu na granicznych przypadkach, gdy wariancja, że ​​nie dbam o krzyże próg dla wartości, którą sprawdzasz. na przykład różnica milisekundy jest mniejsza niż sekunda, ale dwa znaczniki czasu przekraczają drugi próg lub próg minutowy lub próg godzinowy. To sprawia, że ​​każde podejście DateFormat z natury jest podatne na błędy.

Zamiast tego, sugerowałbym porównywanie rzeczywistych milisekundowych znaczników czasu i dostarczenie delty różnicowej wskazującej, co uważasz za akceptowalną różnicę między dwoma obiektami daty.Nadmiernie gadatliwy przykład następująco:

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

Coś jak to może działać:

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

Korzystanie JUnit 4 można również realizować dopasowywania dat testowania według wybranego precyzją. W tym przykładzie program dopasowujący przyjmuje jako wyrażenie parametr formatujący łańcuch. Kod nie jest krótszy dla tego przykładu. Jednak klasa Matchera może być ponownie wykorzystana; a jeśli nadasz mu nazwę opisującą, możesz udokumentować zamiar testem w elegancki sposób.

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()); 
     } 
    }; 
} 
4

W JUnit można zaprogramować na dwa sposoby twierdzić coś takiego:

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

Wystarczy porównać części dat zainteresowany porównując:

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

Podobało mi się to podejście o wiele lepiej niż przy użyciu edytora daty. Jedynym problemem jest to, że konkretne pola pobierające w Date są przestarzałe. Lepiej użyć Kalendarza, aby zrobić to samo. – kfox

+0

Ach, dobrze zauważyć, że te metody są przestarzałe. Zaktualizowałem swoją odpowiedź za pomocą alternatywnego kodu, aby przekonwertować i porównać obiekty kalendarza. –

1

Jeśli uzywasz Joda możesz użyć Fest Joda Time.

+3

Czy możesz podać więcej informacji, w jaki sposób powinno to zostać wdrożone? Inaczej należy to przekształcić na komentarz. –

-1

rzucam obiekty do java.util.Date i porównać

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

Spowoduje to niepowodzenie asertu z powodu precyzji. – TechCrunch

0

Zamiast new Date bezpośrednio, można stworzyć mały współpracownika, który można drwić się w teście:

public class DateBuilder { 
    public java.util.Date now() { 
     return new java.util.Date(); 
    } 
} 

Tworzenie człon DateBuilder i zmienić połączenia z new Date do 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(); 
    } 
} 

Główną metodą będzie produkować:

Dates are the same: false 

W teście można wstrzyknąć niedopałek DateBuilder i niech zwraca żadnej wartości chcesz. Na przykład z Mockito lub anonimowej klasy, która nadpisuje 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(); 
    } 
} 
2

użytku AssertJ twierdzeń dla 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())); 

komunikat braku test jest bardziej czytelny

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. 
+1

AssertJ działa również dla java.util.date: 'assertThat (new Date (2016 - 1900, 0, 1,12,13,14)). IsEqualTIIgnoringMillis (" 2016-01-01T12: 13: 14 ");' –

+0

dobrze wiedzieć, thx Dan – ysl

0

Konwertuj daty na String za pomocą SimpleDateFromat, określ w konstruktorze wymagane pola daty/godziny i porównaj wartości ciągu:

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

JUnit posiada wbudowany twierdzenie porównywania dwuosobowe oraz określenie jak blisko muszą być. W tym przypadku delta mieści się w zakresie milisekund, które uznaje się za równoważne datom. To rozwiązanie nie ma warunków brzegowych, mierzy bezwzględną wariancję, może łatwo określić precyzję i nie wymaga żadnych dodatkowych bibliotek ani kodu do napisania.

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

Uwaga Musi być 100,0 zamiast 100 (lub oddanych do podwojenia jest potrzebna), aby zmusić go porównać je jako podwójne.

Powiązane problemy