今日は、AspectJでDbCを実際にちょろっとやってみたサンプルを紹介したいと思います。サンプルとして簡単なスタックの実装を考えてみました。
以下のようなインターフェースがあるとします。
このインターフェースに対するContractをAspectJで記述すると以下のようになります。
事前条件、事後条件のチェックをJUnitのAssertクラスを使って行っています。ですから、このアスペクトをWeavingしてテストすれば、JUnitのテストケースにはアサーションのコードを一切書かなくて良くなります。
JUnitでは一般的に一つ一つの入力データに対する具体的な結果を検証するのに対し、DbCでは一般的なルールとして記述するので、記述自体の難易度はDbCの方が難しいです。しかし、クラスの仕様としての明解さ、厳密さはこちらの方が上だと思います。
よく言われるJUnitのメリットの一つに、テストケースからクラスの使い方が分かる、というのがあります。しかし、テストケースからクラスの使い方を知る、という作業は、具体的な入力値と出力値から一般的な仕様を頭の中でリバースエンジニアリングする作業だと思います。DbCにおいては、記述されるのは仕様そのものなので、より直截なアプローチだと思います。
次回は、Stackの実装とJUnitテストケースを紹介します。
以下のようなインターフェースがあるとします。
package sample.dbc; public interface Stack { /** * スタックのサイズを返す. */ int size(); /** * スタックに引数のオブジェクトをPushする. * @param o Pushするオブジェクト */ void push(Object o); /** * スタックからオブジェクトをpopする. */ Object pop(); /** * スタックの一番上のオブジェクトを返す. */ Object peek(); }特に説明する必要も無いですね。
このインターフェースに対するContractをAspectJで記述すると以下のようになります。
package sample.dbc; import junit.framework.Assert; public aspect StackContract { declare parents: StackContract extends Assert; /** * Invariant. */ after(Stack s) : !within(StackContract) && target(s) && call(public * Stack.*(..)) { assertTrue(s.size() >= 0); } /** * Stack#push. */ void around(Stack s, Object o) : target(s) && args(o) && call(public void Stack.push(Object)) { //@pre assertNotNull(o); //@post int oldsize = s.size(); proceed(s, o); assertEquals(oldsize + 1, s.size()); assertSame(o, s.peek()); } /** * Stack#pop. */ Object around(Stack s) : target(s) && call(public void pop()) { //@post int oldsize = s.size(); Object oldpeek = s.peek(); Object rv = proceed(s); if(oldsize > 0) { assertSame(oldpeek, rv); assertEquals(oldsize - 1, s.size()); } else { assertNull(rv); assertEquals(0, s.size()); } return rv; } }
事前条件、事後条件のチェックをJUnitのAssertクラスを使って行っています。ですから、このアスペクトをWeavingしてテストすれば、JUnitのテストケースにはアサーションのコードを一切書かなくて良くなります。
JUnitでは一般的に一つ一つの入力データに対する具体的な結果を検証するのに対し、DbCでは一般的なルールとして記述するので、記述自体の難易度はDbCの方が難しいです。しかし、クラスの仕様としての明解さ、厳密さはこちらの方が上だと思います。
よく言われるJUnitのメリットの一つに、テストケースからクラスの使い方が分かる、というのがあります。しかし、テストケースからクラスの使い方を知る、という作業は、具体的な入力値と出力値から一般的な仕様を頭の中でリバースエンジニアリングする作業だと思います。DbCにおいては、記述されるのは仕様そのものなので、より直截なアプローチだと思います。
次回は、Stackの実装とJUnitテストケースを紹介します。