速攻解説! JUnit 4.4 - 新アサーションメソッド『assertThat』の用途とは」で触れたとおり、JUnit 4.4では新しいアサーションメソッドとしてassertThatが導入されたわけだが、同じく新たに導入されたAssumeクラスを併用すると処理の「前提条件」を表現することもできる。また同様の処理の拡張的な使い方になるが、JUnit 4.4では「Theory」という概念が導入されている点も興味深い。本稿では、その2つの機能について紹介したい。

Assume.assumeThatで実行環境の「前提条件」を表現

テストコードを作成してユニットテストをクリアしたのにもかかわらず、アプリケーションを他の環境に移した途端に問題が現れるというのは実によくある話だ。これはユニットテストコードを作成したデベロッパが暗黙の前提として仮定しているものが、自分の開発環境とほかの環境とで食い違っていることから発生する。

こういった状況を避けるために、実行される環境や「前提条件」そのものをユニットテストコードとして記述するというのは、なかなかいい方法だ。これまでのJUnitでは手軽にはこういったコードを作成することはできなかったわけだが、前提条件を表現するAssumeクラスが追加されたJUnit 4.4からは「前提条件」も記述できるようになった。JUnit 4.4リリースノートに実装例が記載されているので次に掲載しておく。

assertThatの使用例(JUnit 4.4リリースノートより抜粋)

import static org.junit.Assume.*

@Test public void filenameIncludesUsername() {
   assumeThat(File.separatorChar, is('/'));
   assertThat(new User("optimus").configFileName(), is("configfiles/optimus.cfg"));
}

@Test public void correctBehaviorWhenFilenameIsNull() {
   assumeTrue(bugFixed("13356"));  // bugFixed is not included in JUnit
   assertThat(parse(null), is(new NullDocument()));
}

@Testを指定したメソッドにAssume.assumeThatで前提条件を記述し、Assert.assertThatで条件をチェックしているわけだ。前提条件を何も記述しない場合と比較して、このようにユニットテストコードとして記述しておけば、実行環境が想定しているものと同じかどうかをテスト時にチェックできるという寸法だ。またこの方法は@Test以外にも@Beforeや@BeforeClassでも同じ効果が得られる。

JUnit 4.4の実装では前提条件やアサーションの条件に合致しているか否かにかかわらず処理が通ってしまうわけだが、これは将来のリリースでは変更され、前提条件をクリアしていない場合にはテスト処理が中止されるようになるとみられる。

AssumeとAssertを活用して新しい状況表現「Theory」を導入

前述されているAssumeとAssertを組み合わせた話と絡んでくるわけだが、JUnit 4.4では実験的ながらも「Theory」と呼ばれる方法が導入されている。これはもともとJUnit 4系向けに開発されていたThe Popper Projectの成果物を取り込んだものだが、デベロッパーに対して明示的なデータ定義のチェックなどの提供するものだ。

本機能についてもJUnit 4.4リリースノートにサンプルが掲載されているので次に抜粋しておく。

Theoryの使用例(JUnit 4.4リリースノートより抜粋)

@RunWith(Theories.class)
public class UserTest {
  @DataPoint public static String GOOD_USERNAME = "optimus";
  @DataPoint public static String USERNAME_WITH_SLASH = "optimus/prime";    

  @Theory public void filenameIncludesUsername(String username) {
    assumeThat(username, not(containsString("/")));
    assertThat(new User(username).configFileName(), containsString(username));
  }
}

上記のサンプルではDataPointに関するの定義が実行されるごとにfilenameIncludesUsernameメソッドが実行されることになる。アサンプションに失敗すればデータポイントは無視されることになるし、仮にすべてのアサンプションをクリアしたとしてもアサーションが失敗すればテストも失敗するというわけだ。

このように記述することで、ユーザ名はスラッシュを含んでいはならないこと(assumeThat(username, not(containsString("/")))で指定)、ユーザファイル名がユーザ名を含んでいる必要があること(assertThat(new User(username).configFileName(), containsString(username))で指定)が明示的にわかるという寸法だ。

JUnit 4.4では外部プロジェクトの成果物を取り込み機能拡張を実現

結局のところJUnit 4.4ではThe Hamcrest Projectの成果物とThe Popper Projectの成果物の両方を取り込んで、実現する表現能力を向上させたバージョンということになるだろう。Assert.assertThatを導入してアサーションメソッドの機能を拡大し、Assumeを導入して前提条件の記述を可能にし、そして「Theory」を使ったより柔軟で強力なアサーションの実現しているというわけだ。

Assumeを使いこなすにこしたことはないが、Assert.assertThatを使ったアサーションを実現するというだけでも効果があるように思える。特に従来のアサーションメソッドとは異なり、より豊富な比較機能を使って、より理解しやすいコードが書けるようになることは採用を検討するに値するメリットと言えるだろう。また比較機能を自分で作成する点も評価できる。JUnitを使っているデベロッパは一度JUnit 4.4の新機能を調査してみてほしい。