jcunit's blog

JCUnitの開発日誌(ログ)です。"その時点での"JCUnit作者の理解や考え、開発状況を日本語で書きます。

テストケースの内容に応じてテストメソッドの実行を制御する方法。

JCUnitのように、テストケースの集合を自動で生成する仕組みを作ったのに、確認すべき内容や実行すべきシナリオがテストケースの内容によって違う場合、それらの確認内容を振り分けるロジックをユーザにif文でちまちま書かせるのはいささかだせえ。

そこで、テストケース(各因子がとる水準)の内容に応じてあるテストメソッドが実行されるべきかどうかを制御できる仕組みを作った。

なおこの機能は0.4.12で利用可能である。

実例と記法。

さっそくだが、例を掲げる。

    public boolean aIsNonZero() {
        return a != 0;
    }

    public boolean discriminantIsNonNegative() {
        return b * b - 4 * c * a >= 0;
    }

    @Test
    @When("aIsNonZero&&discriminantIsNonNegative")
    public void solveEquation() {
        // 1. solve it.
        // 2. make sure the solutions are precise.
    }

WhenはAnnotationとして、このテストメソッドが実行されるべき条件を論理式として記述する。

以下、使い方を述べる。

単純な真偽の状態

以下のように書く。

    @When("cond1")

こう書くとメソッドcond1が呼ばれ、それがtrueを返したときに限り、この@Whenアノテーションが付されたテストメソッドが実行される。
cond1が存在しない、publicでない、staticになっている、戻り値がbooleanではない、なんらかの引数をとる場合には、テストは速やかに失敗する。
ちなみに、@Whenアノテーションに与えられた文字列内のすべての空白文字は無視される。

否定

    @When("!cond1")

上記の否定はメソッド名の前に'!'をつけることで表現できる。

論理積

cond1かつcond2のような論理積は以下のように表現できる。

    @When("cond1&&cond2")

論理和

cond1またはcond2のような論理和は以下のように表現できる。

    @When({"cond1","cond2"})

これらの組み合わせ

上述の記法をもちいれば、利用可能な条件を組み合わせることで得られるすべての可能な論理式(またはそれに等価な論理式)を表現できる。

    @When({"cond1&&cond2", "cond3", "!cond1&&cond4"})

これは

(cond1 && cond2) || cond3 || (!cond1&&cond4)

に対応する。

なお、各々の文字列の中ではカッコを使うことはできない。

まとめ

JCUnitのように複数のテストケースを自動的に生成するフレームワークでは、生成されたテストケースに応じて確認すべき内容が変化することがある。
このような場合分けを自動的に行う方法を導入した。

使い方の例を再掲すると以下のとおり。

    @Test
    @When({ "aIsNonZero&&discriminantIsNonNegative&&coefficientsAreValid" })
    public void whenSolveEquation$thenSolved() {
      whenSolveEquation();    // <-- この辺を人間が書く代わりにJCUnitが勝手に実行するようにしたらいいのでは?
      thenSolved();           // <-- 
    }

今後

BDD的な応用については、いくつかアイデアがなくもないが、所詮は耳学問でその上、切実な必要も、その利益に対する実感も今のところあまりない。
現在のところは、github上でチケットをファイルするにとどめ、今後、作者の理解が進み興味が赴いた場合にちまちまと作ることにする。*1
読者のフィードバックを待つ次第である。

@Whenアノテーションについては、選言標準型に論理式を変換することで、いかなる論理式も表現可能であるとは言え、そうした手間をユーザにかけさせるのもいかがなものかというのはもっともな指摘である。
Antlrを使って、括弧をもちいたより複雑な論理式を解析させるのも可能だが、一方で汎用的なテストフレームワークであるJCUnitとしては、外部ライブラリへの依存はできるだけ持ちたくない。痛し痒しというところだ。*2

*1:https://github.com/dakusui/jcunit/issues/7

*2:ちなみにJCUnitがTupleの内容を文字列化するときに、JacksonもGSONも使っていないのはこのためだ