ミリ秒が異なるために失敗する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
クラスパスに 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 Date/Time APIを使用している場合、 AssertJisCloseTo
アサーションが追加されました。
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());
次のようなことができます:
assertTrue((date1.getTime()/1000) == (date2.getTime()/1000));
文字列の比較は不要です。
JUnitでは、次のように2つのアサートメソッドをプログラムできます。
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にサポートがあるかどうかはわかりませんが、それを行う1つの方法です。
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秒未満ですが、2つのタイムスタンプが2番目のしきい値、分しきい値、または時間しきい値を超えています。これにより、DateFormatアプローチは本質的にエラーが発生しやすくなります。
代わりに、実際のミリ秒のタイムスタンプを比較し、2つの日付オブジェクトの許容差と見なすものを示す分散デルタを提供することをお勧めします。過度に詳細な例を次に示します。
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);
}
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.
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を使用している場合は、 Fest Joda Time を使用できます。
JUnitには、doubleを比較し、doubleの必要性を指定するための組み込みアサーションがあります。この場合、デルタは、同等の日付とみなされるミリ秒以内です。このソリューションには境界条件がなく、絶対分散を測定し、精度を簡単に指定でき、追加のライブラリやコードを記述する必要がありません。
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ではなく100.0でなければなりません(またはdoubleへのキャストが必要です)。
比較したい日付部分を比較するだけです。
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));
SimpleDateFromatを使用して日付を文字列に変換し、コンストラクターで必要な日付/時刻フィールドを指定し、文字列値を比較します。
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String expectedDate = formatter.format(dateOne));
String dateToTest = formatter.format(dateTwo);
assertEquals(expectedDate, dateToTest);
日付を比較するときに必要な精度レベルを選択できます。例:
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);
ここで終わるいくつかのグーグルに役立つかもしれない小さなクラスをしました: https://stackoverflow.com/a/37168645/5930242
これが私のために仕事をしてくれたユーティリティ関数です。
private boolean isEqual(Date d1, Date d2){
return d1.toLocalDate().equals(d2.toLocalDate());
}
このような何かが動作する可能性があります:
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();
}
}
_
Mainメソッドは以下を生成します:
_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();
}
}
_