BDDフレームワーク「instinct」を触ってみる
Java向けのBDDフレームワーク「Instinct」というのがあるらしいので、ちょっと試してみた。ブクマには「誰かの評価待ち」っていう何ともウィンプなコメントを残したが、あまりにあんまりなので。
About
Instinct is a Behaviour Driven Development (BDD) framework for Java. Inspired by RSpec, Instinct provides flexible annotation of contexts, specifications and actors; automatic creation of test doubles and test subjects; a state and behaviour expectation API; JUnit test runner integration; Ant support and an IntelliJ IDEA plugin.
Google Code Archive - Long-term storage for Google Code Project Hosting.
InstictはRSpecにインスパイアされた、Java向けのBDDフレームワークです。
- コンテンツや仕様(specitication)、アクター*1への柔軟なアノテーション記述
- テスト対象やテスト・ダブルの自動生成
- 期待されるAPIの振る舞いの記述
- JUnitの拡張
- AntやIntelliJIDEA pluginによるサポート
Google Code Archive - Long-term storage for Google Code Project Hosting.:俺々和訳
記法をわかりやすくしたってのとモック利用をサポートしたってのが良さそうな点。JUnitの拡張でもあるので、既存のJUnit関連ツールもそのまま使える。のかな。
とりあえず動かしてみる
Google Code Archive - Long-term storage for Google Code Project Hosting.の Download から Example Project を落として展開、eclipseで"Create project from existing source"からプロジェクトを新規作成する。
必要なモノは全て入ってるはず。起動方法は色々ある。
- 付属のAnt build.xmlから起動する。独自起動の-run-specsと、junit3拡張およびjunit4拡張を利用した記述が用意されていて、とりあえず全部動くようになってる。
- JUnitのテストケースとして、そのまま実行できる。eclipseでは"Shift + Alt + x , t"で起動。
- Javaから呼び出す場合、com.googlecode.instinct.runner.TextRunner.runContexts(final Class... contextClasses)を利用する。例えば、↓のように。
public class StackBehaviourRunner { public static void main(String[] args) { TextRunner.runContexts( AnEmptyStack.class, ANonEmptyStack.class); } }
- テキストファイルに設定を書き込んで…みたいにも呼び出せるらしい。
特徴
サンプルを読む
com.googlecode.instinct.example.stack.AnEmptyStack を例に。日本語コメントでよごしてます。
package com.googlecode.instinct.example.stack; import static com.googlecode.instinct.expect.Expect.expect; import com.googlecode.instinct.integrate.junit4.InstinctRunner; import com.googlecode.instinct.marker.annotate.BeforeSpecification; import com.googlecode.instinct.marker.annotate.Dummy; import com.googlecode.instinct.marker.annotate.Specification; import static com.googlecode.instinct.marker.annotate.Specification.SpecificationState.PENDING; import com.googlecode.instinct.marker.annotate.Subject; import org.junit.runner.RunWith; //↓の記述でJUnitと連携するご様子。 @RunWith(InstinctRunner.class) public final class AnEmptyStack { //@Subjectは記述対象。 //implimention属性を指定でき、指定すると自動生成(auto-creationとかauto-wiring:自動配線と呼んでいる)が出来る…とのことだが、よくわからない。 @Subject private Stack<Object> stack; //@Dummyで初期化不要なダミーオブジェクトとして扱える。 //この場合、例えばobject.hashCode()とか呼んだ時点で例外が投げられる。中身はProxyなクラス。 //@Dummyを@Mockと書き換えても動作可能で、@Mockとした場合、object.hashCode()などは呼べる。 //Mockも中身はProxyなクラス。 //例えば"@Mock private java.util.Date object"とすると、toString()で"object"みたいに変数名が表示される。 //@Dummyを@Stubと書き換えても動作可能で、Stubだけ、中身はProxyなクラスではない。 //例えば"@Stub private java.util.Date object"とすると、"Thu Jan 01 09:00:00 JST 1970"なDateインスタンスになる。 @Dummy private Object object; //@BeforeSpecificationは、それぞれのSpecificationを実行する前に実行される。 @BeforeSpecification void before() { stack = new StackImpl<Object>(); } //@Specificationは振舞い仕様の記述であり、テストの記述。 @Specification void isEmpty() { expect.that(stack.isEmpty()).isTrue(); } @Specification void isNoLongerBeEmptyAfterPush() { stack.push(object); expect.that(stack.isEmpty()).isFalse(); } //expectedException属性で、期待する例外を定義。その場合、withMessage属性で期待するメッセージを定義できるらしいが、ここまでは使わないような気もする。 @Specification(expectedException = IllegalStateException.class, withMessage = "Cannot pop an empty stack") void throwsExceptionWhenPopped() { stack.pop(); } //state属性で、「未記述」「記述完了」「保留」などの状態を付けられる。 //「未記述」「保留」の場合、そのテストは実行されない。 @Specification(state = PENDING) void hasSomeNewFeatureWeHaveNotThoughtOfYet() { expect.that(true).isFalse(); } }
雑感
- モック等のサポートが簡単で嬉しいが、Dummy,Stub,Mockの使い分けがまだよくわからない。
- Fixtureでテスト用のデータセットを定義できるらしいが、そこまで試してない。
- Java向けBDDフレームワークであるところのJBehaveよりも、記述はしやすいと思う。アレは今考えると、matcherとか例外のためのブロックとか、記述は面倒だった。
- csvreader.bddとcsvreader.junitで、同じテスト内容での記述の違いの例示があって、親切。junitでの各テストケースはテスト対象クラス向けの記述になっているのに対し、bddは「csvファイルがこの場合には…」という視点でクラスを分けて記述されている。仕様クラス、みたいな感じだろか。
- 全体的に使いやすい直感。サンプルは親切だし、ドキュメントの量が少ない割にわからない感が少ないし。「ある程度使う」であれば、このくらいの知識で十分かも。
- というわけで、より詳しい諸兄の評価待ち(笑)
*1:アクターとは、振舞いを記述する対象物や記述用に一時的に使用するのダブル(=モック、スタブ、ダミー)らのこと。see also http://code.google.com/p/instinct/wiki/Terminology