日々常々

ふつうのプログラマがあたりまえにしたいこと。

JUnitでテスト名の制約を取っ払う

2020-06-11追記
本稿の対象はJUnit4です。JUnit5で同等のアノテーション`@DisplayName(value)` が追加されているため、このような小細工は必要ありません。

発端は[twitter:@ayato_p]さんのツイートでございます。「JUnitのテストメソッド名を日本語で書く」のはそれほど奇異な話ではありません。

JUnit実践入門でも「日本語のメソッド名を使うメリット」と言うコラムがあり、サンプルコードのテストメソッド名は平然と日本語で書かれています。
[isbn:978-4774153773:detail]

しかし日本語メソッド名なので、当然のようにメソッド名に由来する問題が幾らかあります。よく詰まるのは「そもそも使えない」ですが、これはVM引数などで解決出来ます。

他に単純なところでは句読点や中黒が使えない所でしょうか。小数点も使えませんし、数字ではじめることも出来ません。こういう制約に引っかかったときは _ で代用するのが常道ですが、使えるなら使いたいですよね。

それGroovyで

@Test
public void "え、Groovyなら余・裕だよ?"() {
    fail("しぱーい")
}

いいから黙れ。……正直、現実解だと思います。使えるなら。Groovyに不自由しない人なら、JUnitをGroovyで書くのはそれほど悪い選択では無いと思います。IDEがどれだけサポートしてくれるか問題はありますが、思ったよりやってくれる感はあります。

GroovyでJUnitを使うことの最大のメリットは、おそらくPowerAssertになります。他にはSpockとの連携・切り替えがシームレスに行えるなどですかね。うっかりstatic import忘れてMissingMethodExceptionとかなったりして、Javaでは見慣れない例外を吐くことも多いので、Groovyに不慣れな人が本番でチャレンジ的に使うものではありません。壊してよいオモチャでやってください。

JUnitでテスト名を指定する

本当の問題に向き合いましょう。
直接の問題は「テストメソッド名に日本語を使うと微妙な制約のせいで思い通り記述出来ない」ですが、別に「テストメソッド名に日本語を使いたい」ではありません。テストメソッド名に日本語を使うことの目的は「レポートなどに表示されるテスト名を日本語にしたい」です。ならば「テストメソッド名の日本語の制約を取っ払いたい」ではなく「テスト名を日本語で指定したい」ととることが出来ます。
メソッド名の制約を解除することはJavaに縛られている限り厳しいですが、テスト名を指定することなら出来ます。こんな感じ。

@Test(name = "テスト名を、文字列で。")
public void test() {
    fail("しぱーい");
}


……ごめん、嘘つきました。Testアノテーションにnameなんてのはありません。いや作ればいけるんですが、JUnit自体に手を入れるのは、出来ない理由はないんですが、うん、はい。
なので(強引)、アノテーションとRunnerを書きます。えい。

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Name {
    String value();
}
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;

public class Names extends BlockJUnit4ClassRunner {
    public Names(Class<?> klass) throws InitializationError {
        super(klass);
    }

    @Override
    protected String testName(FrameworkMethod method) {
        Name name = method.getAnnotation(Name.class);
        if (name == null) {
            return method.getName();
        }
        return name.value();
    }
}

実行してみる

後はこのRunnerとアノテーションを使用したテストを書くだけ。

import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.fail;

@RunWith(Names.class)
public class NamesSample {

    @Test
    public void normal() {
    }

    @Test @Name("123.456")
    public void 小数点付き() {
    }
    @Test @Name("このテストは、どのテスト?")
    public void 句読点と疑問符() {
        fail("しぱーい");
    }
}

Eclipseで実行したらこんな感じになる。最近はIntelliJ IDEA派だけど。

まとめ

  • どうしても小数点とか使いたかったら名前を付けれるようにするって選択もあるよ。
  • JUnitのカスタムRunnerも、簡単に作るだけなら簡単。
  • Eclipseとかで、ビューのテスト名をダブルクリックしてもテストコードのメソッドに移動しない。(これはプラグインの実装なんでなんとも)
  • Groovyでおk