【Java】テストケースIDをスマートに判定する

2023/09/05に公開

はじめに

JUnitのコードを書いていると、以下のように判定しテストケース固有のコードを書くことがある。
XxxTestCaseIdはenumとしている。その方がIDEで補完入力しやすいし、switch文(式)でも扱いやすい。
今回の手法とswitch文(式)のどちらを使うかはケースバイケースで。

// 全3ケースのうち、1ケース目と2ケース目のみ固有条件(あるいは期待値)を定義
if (testCaseId == XxxTestCaseId.XXXX_0001 || testCaseId == XxxTestCaseId.XXXX_0002) {

}

やりたいこと&実現方法

これをスマートに表現する方法を模索。
インターフェース&defaultメソッドを使って、enumの拡張を行う。
インタフェース&Utilを使っても可能だが、defaultメソッドを使った拡張のほうが読みやすい。

例えばテストケースIdが1 or 2 or 3ケース目のどれかに該当することを示すコードを書きたい場合、ユーティリティクラスを使うと以下のようになるが、

// ケース番号判定をユーティリティクラス化した例
if (TestUtil.ifNo(testCaseId, 1, 2, 3)) {  

インターフェース&defaultメソッドで拡張するとこのようにスマートに書くことができる。

// enumを拡張した例
if (testCaseNo.is(1, 2, 3)) {
// 否定形も可
if (testCaseNo.isNot(1, 2, 3)) {
// 範囲指定も可
if (testCaseNo.isInRange(1).to(3)) {

コード例

以下、インターフェースの定義例。

public interface TestCaseIdEx {

  String name();

  /**
    * テストケースIDの通番部分を数値化して取得する。
    * isメソッドで通番参照するためのメソッド。
    */
  default int getCaseNo() {
    // 通番部分を取得(この例では下4桁)
    return Integer.parseInt(name().substring(name().length() - 4));
    // apache commonsのStringUtils.right(name(), 4)を使うと安全
  }

  default boolean is(int... no) {
    if (no == null) {
      return false;
    }

    int thisCaseNo = getCaseNo();

    return Arrays.stream(no).anyMatch(n -> n == thisCaseNo);
  }

  default boolean isNot(int... no) {
    return !is(no);
  }

  // 範囲指定用
  // // テストケース番号3~7
  // if (testCaseId.isInRange(3).to(7)) {
  default TestCaseNoRange isInRange(int beginNo) {
    var range = new TestCaseIdRange();
    range.caseNo = getSerialNo();
    range.beginNo = beginNo;

    return range;
  }

  public class TestCaseIdRange {
    private int caseNo;
    private int beginNo;

    boolean to(int endNo) {
      return beginNo <= caseNo && caseNo <= endNo;
    }
  }
}

利用するときの定義。インターフェースをimplementsするだけ。

enum XxxTestCaseId implements TestCaseIdEx {
  XXXX_0001,
  XXXX_0002,
  XXXX_0003,}

Discussion