Mam test JUnit, który kończy się niepowodzeniem, ponieważ milisekundy są różne. W tym przypadku nie obchodzą mnie milisekundy. Jak mogę zmienić precyzję potwierdzenia, aby ignorować milisekundy (lub dowolną precyzję, którą chciałbym ustawić)?
Przykład niezgodnego twierdzenia, które chciałbym spełnić:
Date dateOne = new Date();
dateOne.setTime(61202516585000L);
Date dateTwo = new Date();
dateTwo.setTime(61202516585123L);
assertEquals(dateOne, dateTwo);
Jeszcze jedno obejście, zrobiłbym to w ten sposób:
assertTrue("Dates aren't close enough to each other!", (date2.getTime() - date1.getTime()) < 1000);
źródło
Istnieją biblioteki, które pomagają w tym:
Apache commons-lang
Jeśli masz Apache commons-lang na swojej ścieżce klas, możesz użyć
DateUtils.truncate
do skrócenia dat do jakiegoś pola.Jest na to skrót:
Pamiętaj, że godziny 12: 00: 00.001 i 11: 59: 00.999 zostałyby obcięte do innych wartości, więc może to nie być idealne rozwiązanie. Do tego jest okrągły:
AssertJ
Począwszy od wersji 3.7.0, AssertJ dodał
isCloseTo
asercje, jeśli używasz interfejsu API daty / czasu Java 8.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 datami Java:
Date d1 = new Date(); Date d2 = new Date(); assertThat(d1).isCloseTo(d2, within(100, ChronoUnit.MILLIS).getValue());
źródło
java.sql.Timestamps
iDateUtils.truncate(...)
pracował dla mnie w Javie 8. Mój szczególny przypadek obejmował technologii baz danych, które nie obsługuje zapisywania jakichkolwiek ziarno drobniejsze niż sekundę, więc miałem porównanie znacznik czasu w pamięci do jednego, który został zapisany pobrane z bazy danych. Znacznik czasu w pamięci miał większą dokładność niż znacznik czasu odczytany z bazy danych.Możesz zrobić coś takiego:
assertTrue((date1.getTime()/1000) == (date2.getTime()/1000));
Nie są potrzebne porównania ciągów.
źródło
W JUnit możesz zaprogramować dwie metody asercji, takie jak ta:
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); } }
źródło
Nie wiem, czy w JUnit jest wsparcie, ale można to zrobić w jeden sposób:
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!"); } } }
źródło
assertEqualDates
, zrobię jej typ zwracaniavoid
i utworzę ostatnią linięassertEquals(d1, d2)
. W ten sposób zachowywałby się tak samo, jak wszystkieassert*
metody JUnit .W rzeczywistości jest to trudniejszy problem, niż się wydaje, z powodu przypadków granicznych, w których wariancja, na której nie zależy Ci, przekracza próg dla sprawdzanej wartości. np. różnica milisekund jest mniejsza niż sekunda, ale dwa znaczniki czasu przekraczają drugi próg, próg minut lub próg godzin. To sprawia, że każde podejście DateFormat jest z natury podatne na błędy.
Zamiast tego sugerowałbym porównanie rzeczywistych milisekundowych znaczników czasu i podanie delty wariancji wskazującej, jaka jest akceptowalna różnica między dwoma obiektami daty. Oto nadmiernie rozwlekły przykład:
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); }
źródło
Korzystając z JUnit 4, możesz również zaimplementować dopasowanie do dat testowania zgodnie z wybraną precyzją. W tym przykładzie element dopasowujący przyjmuje jako parametr wyrażenie formatu ciągu. W tym przykładzie kod nie jest krótszy. Jednak klasa dopasowania może zostać ponownie wykorzystana; a jeśli nadasz mu opisową nazwę, możesz w elegancki sposób udokumentować zamiar za pomocą testu.
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()); } }; }
źródło
użyj asercji AssertJ 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 o niepowodzeniu testu 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.
źródło
assertThat(new Date(2016 - 1900, 0, 1,12,13,14)).isEqualToIgnoringMillis("2016-01-01T12:13:14");
Jeśli używałeś Joda, możesz użyć Fest Joda Time .
źródło
Po prostu porównaj części dat, które chcesz porównać:
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));
źródło
JUnit ma wbudowaną asercję do porównywania podwójnych i określania, jak blisko muszą się znajdować. W tym przypadku delta mieści się w zakresie milisekund, które uznajesz za równoważne. To rozwiązanie nie ma warunków brzegowych, mierzy bezwzględną wariancję, może z łatwością określić precyzję i nie wymaga pisania dodatkowych bibliotek ani kodu.
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 wymagane jest rzutowanie na podwojenie), aby zmusić go do porównania ich jako podwójnych.
źródło
Podczas porównywania dat możesz wybrać żądany poziom dokładności, np .:
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);
źródło
Coś takiego może działać:
assertEquals(new SimpleDateFormat("dd MMM yyyy").format(dateOne), new SimpleDateFormat("dd MMM yyyy").format(dateTwo));
źródło
Zamiast używać
new Date
bezpośrednio, możesz utworzyć małego współpracownika, którego możesz wyszydzić w swoim teście:public class DateBuilder { public java.util.Date now() { return new java.util.Date(); } }
Utwórz składową DateBuilder i zmień wywołania z
new Date
nadateBuilder.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łówna metoda da:
Dates are the same: false
W teście możesz wstrzyknąć kod
DateBuilder
i pozwolić mu zwrócić dowolną wartość. Na przykład z Mockito lub anonimową klasą, która zastępujenow()
: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(); } }
źródło
Konwertuj daty na ciąg przy użyciu SimpleDateFromat, określ w konstruktorze wymagane pola daty / godziny i porównaj wartości ciągów:
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String expectedDate = formatter.format(dateOne)); String dateToTest = formatter.format(dateTwo); assertEquals(expectedDate, dateToTest);
źródło
Zrobiłem małą klasę, która może być przydatna dla niektórych pracowników Google, którzy kończą tutaj: https://stackoverflow.com/a/37168645/5930242
źródło
Oto funkcja narzędziowa, która wykonała pracę za mnie.
private boolean isEqual(Date d1, Date d2){ return d1.toLocalDate().equals(d2.toLocalDate()); }
źródło
rzucam obiekty na java.util.Date i porównuję
źródło