無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

Jbehaveを使ってみた。

るびまのRSpecの記事を見たときから密かに気になってた振舞駆動開発 (Behaviour Driven Development:BDD)ですが、IBM developerWorks ビヘイビア駆動開発を舞台にした冒険より「Jbehave」というJavaでBDDするライブラリがあるらしいのでちょっと使ってみました。

振舞駆動開発とは何か。何がうれしいのか。

るびまのRSpecの記事で熱く語られています。
個人的なイメージはこんな感じ。注:間違っている可能性大です。真に受けないでくださいね。

  • Jbehaveを使うと、API仕様を書くのと同じ文法でテストが書ける!
  • APIの詳細仕様」=「振る舞いを定義したコード」=「テストケース」になる!
  • なので、「振る舞いを定義したコード」さえ書けばあとの2つはいらない→早く帰れる!

Jbehaveを使ってみる

Jbehaveを使ったテスト手順の概要は次の通りです。

  1. テスト対象クラスのAPIだけを書く。
  2. Behaviorクラスを書く。
    • Behaviorクラスは「テストクラス名Behavior」の名前を持つPOJO
    • 親クラス等の指定は特にない。
  3. Behaviorクラスにメソッドを追加して、テストクラスの振る舞いを記述していく。
    • 「振る舞い」ごとに「should」メソッドを追加し、振る舞いを検証するコードを書く。
    • 評価にはEnsureクラスのメソッドを使う。
  4. Behaviorクラスを動かす。そしてエラーになるのを確認。
  5. テスト対象クラスを実装する。
  6. Behaviorクラスを動かす。テストに合格することを確認。

以下で KittenクラスのテストをJbehaveを使ってやってみます。

1.テスト対象クラスのAPIを書く。

Kittenクラスを定義し、APIを書きます。

/**
 * ねこ
 *
 * @version $Revision:$
 * @author  $Author:$
 */
public class Kitten {

    /**状態*/
    private String state;

    /***
     * 状態を取得する。
     * @return 状態
     */
    public String getState () {
        return state;
    }

    /**
     * ジャンプ。
     * <ul>
     *  <li>ジャンプしたあとは状態が"jumping"になる。</li>
     * </ul>
     * @throws IllegalStateException 走っている場合
     */
    public void jump () {}

    /**
     * 走る。
     * <ul>
     *  <li>走ったあとは状態が"running"になる。</li>
     * </ul>
     * @throws IllegalStateException ジャンプ中の場合
     */
    public void run () throws IllegalStateException {}

    /**
     * 止まる。
     * <ul>
     *  <li>止まったあとは状態が"stopped"になる。</li>
     * </ul>
     */
    public void stop () {}
}
2.Behaviorクラスを書く。

Behaviorクラスを書きます。

  • Kittenクラスの振る舞いなので名前はKittenBehaviorとします。
  • publicな「should」メソッドを定義し、振る舞いの検証コードを書きます。
  • 振る舞いの検証にはEnsureを使います。また、Ensureでの値の比較にはUsingMatchersを使います。(抽象クラスなので、継承して使う必要があります。サンプルでは無名クラスとして継承しインスタンス化しています。)
  • 振る舞いの英訳が面倒なのでshouldメソッドは日本語でいいよね。
import org.jbehave.core.Block;
import org.jbehave.core.Ensure;
import org.jbehave.core.mock.UsingMatchers;

/**
 * {@link Kitten}の振る舞い。
 *
 * @version $Revision:$
 * @author  $Author:$
 */
public class KittenBehavior {

    private static final UsingMatchers m = new UsingMatchers() {};

    public void should_初期状態はstopped () {
        Kitten kitten = new Kitten();
        Ensure.that( kitten.getState(), m.is( "stopped" ) );
    }

    public void should_走ったあとは状態がrunningになる () {
        Kitten kitten = new Kitten();
        kitten.run();
        Ensure.that( kitten.getState(), m.is( "running" ) );
    }
    public void should_ジャンプしたあとは状態がjumpingになる () {
        Kitten kitten = new Kitten();
        kitten.jump();
        Ensure.that( kitten.getState(), m.is( "jumping" ) );
    }
    public void should_止まったあとは状態がstoppedになる () {
        Kitten kitten = new Kitten();
        kitten.stop();
        Ensure.that( kitten.getState(), m.is( "stopped" ) );
    }

    public void should_走っている時にジャンプした場合IllegalStateExceptionになる ()
    throws Exception {
        final Kitten kitten = new Kitten();
        kitten.run();
        Ensure.throwsException( IllegalStateException.class, new Block() {
            public void run () throws Exception {
                kitten.jump();
            }
        } );
    }
    public void should_ジャンプ中に走った場合IllegalStateExceptionになる ()
    throws Exception {
        final Kitten kitten = new Kitten();
        kitten.jump();
        Ensure.throwsException( IllegalStateException.class, new Block() {
            public void run () throws Exception {
                kitten.run();
            }
        } );
    }
}
3.Behaviorクラスを動かす。

org.jbehave.core.Runを使ってBehaviorクラスを動かし、テストに失敗するのを確認します。以下のコマンドを実行。

java -cp jbehave.jar org.jbehave.core.Run KittenBehavior

実行結果です。全滅です。

FFFFFF
Time: 0.343s

Failures: 6.

1) KittenBehavior should_ジャンプしたあとは状態がjumpingになる:
VerificationException: 
Expected: same instance as <jumping>
but got:  <null>: 
	at org.jbehave.core.mock.UsingMatchers.fail(UsingMatchers.java:300)
	at org.jbehave.core.mock.UsingMatchers.ensureThat(UsingMatchers.java:229)
    .... 省略

2) KittenBehavior should_止まったあとは状態がstoppedになる:
VerificationException: 
Expected: same instance as <stopped>
but got:  <null>: 
	at org.jbehave.core.mock.UsingMatchers.fail(UsingMatchers.java:300)
	at org.jbehave.core.mock.UsingMatchers.ensureThat(UsingMatchers.java:229)
	at org.jbehave.core.mock.UsingMatchers.ensureThat(UsingMatchers.java:237)
    .... 省略

3) KittenBehavior should_ジャンプ中に走った場合 illegal state exceptionになる:
VerificationException: should have thrown java.lang.IllegalStateException: 
	at org.jbehave.core.mock.UsingMatchers.fail(UsingMatchers.java:300)
	at org.jbehave.core.Ensure.throwsException(Ensure.java:101)
    .... 省略

4) KittenBehavior should_初期状態はstopped:
VerificationException: 
Expected: same instance as <stopped>
but got:  <null>: 
	at org.jbehave.core.mock.UsingMatchers.fail(UsingMatchers.java:300)
	at org.jbehave.core.mock.UsingMatchers.ensureThat(UsingMatchers.java:229)
    .... 省略

5) KittenBehavior should_走っている時にジャンプした場合 illegal state exceptionになる:
VerificationException: should have thrown java.lang.IllegalStateException: 
	at org.jbehave.core.mock.UsingMatchers.fail(UsingMatchers.java:300)
	at org.jbehave.core.Ensure.throwsException(Ensure.java:101)
    .... 省略

6) KittenBehavior should_走ったあとは状態がrunningになる:
VerificationException: 
Expected: same instance as <running>
but got:  <null>: 
	at org.jbehave.core.mock.UsingMatchers.fail(UsingMatchers.java:300)
	at org.jbehave.core.mock.UsingMatchers.ensureThat(UsingMatchers.java:229)
    .... 省略

Total: 6. Failures: 6.
4.テスト対象クラスを実装する。

テストに通るように、Kittenクラスを実装します。

/**
 * ねこ
 *
 * @version $Revision:$
 * @author  $Author:$
 */
public class Kitten {

    /**状態*/
    private String state = "stopped";

    /***
     * 状態を取得する。
     * @return 状態
     */
    public String getState () {
        return state;
    }

    /**
     * ジャンプ。
     * <ul>
     *  <li>ジャンプしたあとは状態が"jumping"になる。</li>
     * </ul>
     * @throws IllegalStateException 走っている場合
     */
    public void jump () {
        if ( "running".equals( state ) ) {
            throw new IllegalStateException();
        }
        this.state = "jumping";
    }

    /**
     * 走る。
     * <ul>
     *  <li>走ったあとは状態が"running"になる。</li>
     * </ul>
     * @throws IllegalStateException ジャンプ中の場合
     */
    public void run () throws IllegalStateException {
        if ( "jumping".equals( state ) ) {
            throw new IllegalStateException();
        }
        this.state = "running";
    }

    /**
     * 止まる。
     * <ul>
     *  <li>止まったあとは状態が"stopped"になる。</li>
     * </ul>
     */
    public void stop () {
        this.state = "stopped";
    }
}
5.再度Behaviorクラスを動かす。

実装したら再度Behaviorクラスを動かします。

java -cp jbehave.jar org.jbehave.core.Run KittenBehavior

実行結果です。

......
Time: 0.391s

Total: 6. Success!