なんか変わるみたい。
挨拶文パクリ
こんばんは!
MavenセントラルにJUnit 4.11-beta-1が来てたことにようやく気が付きました!
で、早速 build.gradle に
dependencies {
testCompile 'junit:junit:4.11-beta-1'
}
などと書いて体験します。
変わったとこ確認
JUnit4.10 までの assertThat さんはこうでした。
public static <T> void assertThat(String reason, T actual, Matcher<T> matcher) { if (!matcher.matches(actual)) { Description description= new StringDescription(); description.appendText(reason); description.appendText("\nExpected: "); description.appendDescriptionOf(matcher); description.appendText("\n got: "); description.appendValue(actual); description.appendText("\n"); throw new java.lang.AssertionError(description.toString()); } }
appendValue はだいたい toString 結果を表示しちゃいますので、gotには actual の文字列表現で固定だったんです。これを誤魔化すために Matcher の describeTo をいじって expected に actual を混ぜ込んだりする涙ぐましい努力をしたりするわけです。でもやっぱ誤魔化しなんですよね、なんか、うん。
JUnit4.11-beta-1 の assertThat さんはこうなってます。
public static <T> void assertThat(String reason, T actual, Matcher<? super T> matcher) { MatcherAssert.assertThat(reason, actual, matcher); }
なんて潔い……。
そしてこの MatcherAssert#assertThat さんは、hamcrest-coreの1.2(4年前)にございました*1 *2。実装はこの通り。
public static <T> void assertThat(String reason, T actual, Matcher<? super T> matcher) { if (!matcher.matches(actual)) { Description description = new StringDescription(); description.appendText(reason) .appendText("\nExpected: ") .appendDescriptionOf(matcher) .appendText("\n but: "); matcher.describeMismatch(actual, description); throw new AssertionError(description.toString()); } }
describeMismatch の実装は Matcher が actual と description を受けていることからもわかる通り、受け取った description に actual を Matcher の都合のいいようにアレして追加する形です。
車輪を再発明しよう
とりあえずこんな感じで書く。
@Test public void testName() throws Exception { List<?> actual = Arrays.asList("H", "O", "G", "E"); assertThat(actual, size(3)); }
size はもちろんオレオレMatcherだ。
private Matcher<Collection<?>> size(final int size) { return new TypeSafeMatcher<Collection<?>>() { @Override public void describeTo(Description description) { description.appendText("number of elements is " + size); } @Override public boolean matchesSafely(Collection<?> actual) { return actual.size() == size; } }; }
これで実行する。テストはこける。結果はもちろん残念なアレだ。
java.lang.AssertionError: Expected: number of elements is 3 got: <[H, O, G, E]>
JUnit4.11-beta-1 ではこんな感じで書けばいいらしい。
//... public void describeMismatch(Collection<?> actual, Description description) { description.appendText("number of elements was " + actual.size()); } //...
書いた所でもちろん何も変わらない。
ならば自分に都合のいい assertThat を作ればいい。
public static <T> void assertThat(String reason, T actual, Matcher<T> matcher) { if (!matcher.matches(actual)) { Description description = new StringDescription(); description.appendText(reason); description.appendText("\nExpected: "); description.appendDescriptionOf(matcher); description.appendText("\n got: "); appendActualDescription(actual, matcher, description); description.appendText("\n"); throw new java.lang.AssertionError(description.toString()); } } private static <T> void appendActualDescription(T actual, Matcher<T> matcher, Description description) throws AssertionError { try { // describeMismatchメソッドがあったらそれ Method describeMismatch = getDescribeMismatchMethod(matcher); if (describeMismatch != null) { describeMismatch.invoke(matcher, actual, description); return; } // 何も無かったら今まで通り description.appendValue(actual); } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { throw new AssertionError(e); } } private static <T> Method getDescribeMismatchMethod(Matcher<T> matcher) throws IllegalArgumentException, IllegalAccessException { for (Method m : matcher.getClass().getMethods()) { if (m.isAnnotationPresent(DescribeMismatch.class)) { return m; } } return null; }
これで。
java.lang.AssertionError: Expected: number of elements is 3 got: number of elements was 4
いけた。ひゃっほう。
早速車輪(describeMismatch)を投影した。捗る。
なんで車輪を再発明したか
インターネットに接続できないからです。最新のライブラリをいきなり使うのは難しかったりするからです。それでも自分で書いたものであれば、なんとなくぼんやり使えたりします。独自技術症候群万歳です。それもどうなのですが、挙動を把握してるならいい気もします。把握してるなら最新のものを使えって話にもなる気もするあたり、穴だらけの理論ですが。
再発明することで挙動を理解できたりもします。「車輪の再発明の効用」です。ちょっとしたものであれば、欲しい機能だけピンポイントで取り入れたりも出来ます。
ここで書いているのはJUnit4.4+Hamcrest1.1とかでもいけたりします。なんでそんなバージョンかと言うと、現場で使えるJUnitのバージョンがそんな感じだったりするから。JUnitをダウンロードできなくてもEclipseに入ってますので使えたりします。さすがにEclipseも使えないってのはあまり無いですし……。多少バージョン古くても、こんな感じであれこれ色々注入してけば、それなりに快適なJUnitライフが送れたりします。この程度の再発明は10分やそこらですしね。
おまけ
今回書いたのはGitHubに上げてたり。上記のだとisとか使っちゃうと途切れちゃうので、中身引っ張りだすようにしたり。やっつけ仕事感パない。完全な車輪が欲しければ、素直にバージョンアップですよ。