JCUnitによる有限状態機械のテスト - 有限状態機械テストの方法(その3)
考えてみれば、ソフトウェアシステムというのは有限状態機械であり、ソフトウェアシステムの仕様を決めるとはその有限状態機械の仕様を決めることに他ならない。
と、いうことは有限状態機械の仕様からテストを自動的に生成して、それを自動的に実施できるなら、人間はソフトウェアシステム自体の設計と開発に集中してあとのことは計算機にやらせればよい、ということにならないだろうか?
なったらいいなあ。
今回のFSMサポートの目的はこの辺だ。
では、有限状態機械の仕様をどのようにJCUnitに伝えればよいだろうか?
ということでいよいよ、JCUnit上で如何にして有限状態機械を定義するかの解説に移る。
jcunit/FlyingSpaghettiMonsterTest.java at develop · dakusui/jcunit · GitHub
public static enum Spec implements FSMSpec<FlyingSpaghettiMonster> { @StateSpec I { @Override public boolean check(FlyingSpaghettiMonster flyingSpaghettiMonster) { return flyingSpaghettiMonster.isReady(); } @Override public Expectation<FlyingSpaghettiMonster> cook(FSM<FlyingSpaghettiMonster> fsm, String dish, String sauce) { return FSMUtils.valid(fsm, COOKED, CoreMatchers.startsWith("Cooking")); } }, @StateSpec COOKED { @Override public boolean check(FlyingSpaghettiMonster flyingSpaghettiMonster) { return flyingSpaghettiMonster.isReady(); } @Override public Expectation<FlyingSpaghettiMonster> eat(FSM<FlyingSpaghettiMonster> fsm) { return FSMUtils.valid(fsm, COOKED, CoreMatchers.containsString("yummy")); } @Override public Expectation<FlyingSpaghettiMonster> cook(FSM<FlyingSpaghettiMonster> fsm, String dish, String sauce) { return FSMUtils.valid(fsm, COOKED, CoreMatchers.startsWith("Cooking")); } },; @ActionSpec public Expectation<FlyingSpaghettiMonster> cook(FSM<FlyingSpaghettiMonster> fsm, String pasta, String sauce) { return FSMUtils.invalid(); } @ParametersSpec public static final Object[][] cook = new Object[][] { { "spaghetti", "spaghettini" }, { "peperoncino", "carbonara", "meat sauce" }, }; @ActionSpec public Expectation<FlyingSpaghettiMonster> eat(FSM<FlyingSpaghettiMonster> fsm) { return FSMUtils.invalid(); } }
途中をすっとばすならば、上記の内部クラスSpecがJCUnitに渡す有限状態機械の仕様そのものだ。
JCUnitはこのSpecの定義内容を解析し、テストを生成し、実行する。
Specは別に内部クラスでなくてもよいし、enumでなくてもよいが以下の条件を満たす必要がある。
- SUTは任意のクラス。
- FSMSpec
インタフェースを実装する必要がある。 - 状態を定義するには、フィールドを定義し、これにStateSpecアノテーションを付す。これらのフィールドは以下の条件を満たす必要がある。
- 状態遷移関数を定義するにはメソッドを定義し、これにActionSpecアノテーションを付す。これにより、SUTにある同名のメソッドが必要なタイミングで自動的に呼ばれるようになる。上掲の例で言うと、"cook"メソッドと"eat"メソッド。これらのメソッドは以下の条件を満たす必要がある。
- 上述の状態遷移関数に渡す引数がある時には、同名のフィールドを定義し、ParamsSpecアノテーションを付す。上掲の例で言うと、"cook"フィールド。このフィールドは以下の条件を満たす必要がある。
うーん。
頑張ってシンプルにしたんだけど、書き下してみると、けっこうややこしいなあ。
なんにしても、有限状態機械を定義するのには必要な情報だし、なれれば難しくはないと思う(多分)
。。続きます。