hamcrest の Matcher を独自拡張 - java.util.Date 版 closeTo
テスト対象クラスの内部で new Date() しているようなコードをテストする場合、テストクラス側で new Date() した値とはミリ秒程度の誤差が出てしまうことがあります。
hamcrest の Matcher で、double の誤差を許容するためのメソッド closeTo があります。これの java.util.Date 版があれば解決するんじゃね? ・・・と思ったのですが、どうやらデフォルトでは提供されていないようです。残念。
というわけで、自作しました。
比較クラスの実装
package enhance; import java.util.Date; import org.hamcrest.Description; import org.hamcrest.Factory; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; /** * Dateの誤差吸収用 {@link Matcher} です。<p> * thanks to {@link org.hamcrest.number.IsCloseTo} */ public class IsCloseTo extends TypeSafeMatcher<Date> { private final long delta; private final Date base; public IsCloseTo(Date base, long errorMillis) { this.base = base; this.delta = errorMillis; } @Override public void describeTo(Description description) { description.appendText("a date value within ") .appendValue(delta) .appendText(" millisecond of ") .appendValue(base); } @Override protected void describeMismatchSafely(Date item, Description mismatchDescription) { mismatchDescription.appendValue(item) .appendText(" differed by ") .appendValue(actualDelta(item)); } @Override protected boolean matchesSafely(Date item) { return actualDelta(item); } private boolean actualDelta(Date item) { long deltaMillis = base.getTime() - item.getTime(); return Math.abs(deltaMillis) <= delta; } @Factory public static Matcher<Date> closeTo(Date operand, long errorMillis) { return new IsCloseTo(operand, errorMillis); } }
static import 用のメソッド実装
package enhance; /** * thanks to {@link org.hamcrest.Matchers}. */ public class Matchers { public static org.hamcrest.Matcher<java.util.Date> closeTo(java.util.Date operand, long errorMillis) { return enhance.IsCloseTo.closeTo(operand, errorMillis); } }
@Factory アノテーションは、つけておくと hamcrest-genarator というツールを実行したときに Matchers クラスの static メソッドを自動生成してくれるようです(たぶん)。今回は別にあってもなくても構わないのですが、元ソースにならって記述しておきました。
実質、 matchesSafely メソッドをオーバーライドして緩い比較をしてやるだけですので、それほど大したことはやっていません。
以下は、(動作テストも兼ねた)実際の使用例です。
package enhance; import static enhance.Matchers.*; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import java.util.Date; import org.junit.Test; public class CloseToTest { @Test public void date版closeToの使用例() { Date actual = Util.getDate(); Date expected = new Date(); assertThat(actual, is(not(expected))); // 通常のDate比較 assertThat(actual, closeTo(expected, 1000)); // 1秒以内の誤差を許容する比較 } static class Util { static Date getDate() { Date now = new Date(); try { Thread.sleep(900); // わざと0.9秒停止 } catch (InterruptedException e) { throw new RuntimeException(e); } return now; } } }