こんにちは、今回は Pharo の sUnit テストフレームワークでの
ユニットテストを見てみましょう。
テストフレームワークはアジャイル開発を 支援するために使われます。
インクリメンタルな開発をして
定期的にコードをテストして
コードの修正でコードに求められる特性や 不変条件を壊していないかを確認します。
変化に備えるということです。
コードを修正したらテストを書いて
テストを再実行することで
どこか壊してしまったり、変えてはいけない 部分を変えてしまっていないかチェックします。
自動化されたテストはこの種のプログラミング を支援する上でとても重要です。
sUnit フレームワークはテストを書くための 特別なフレームワークです。
sUnit は 4 つのクラスで実装されています。 とてもシンプルです。
sUnit は最初、ケント・ベックによって 開発されました。
そして他の言語の数々のテストフレームワーク を生むきっかけになりました。
例えば、JUnitもその1つです。
では、テストとは何でしょう？
テストを書くには 3 段階があります。
第 1 段階では、コンテキストを作ります。 例えば、空集合です。
第 2 段階では、刺激を作ります。
ここでは、さきほど作った集合に
同じ要素を 2 度追加しようとしています。
そして第 3 段階では 得られた結果をテストして
この集合が要素を 1 つだけ含んでいる ことを確かめます。
集合オブジェクトは同じ要素を 1 つしか 持つことができません。
同じ要素は 2 つ持てないのです。
なので、結果をテストして 不変条件が破られていないことを確認します。
テストを書く例題です。
TestCase のサブクラスを書きます。 ここでは SetTestCase です。
集合をテストするものです。 testAdd メソッドを定義します。
テストメソッドの名前は test で始まります。
まず、コンテキストを設定します。
Set クラスのインスタンスを作ります。 空集合です。
同じ要素を
2 回追加します。
5 を 2 回。 ここと、ここで。
そしてチェックします。
チェックには assert: を使います。
集合のサイズが
1 であれば、テストは成功です。 要素が 1 つだけ追加されたということです。
この式を使って
テストを実行することができます。 このテストで条件が守られているかを
つまり assert: の式が true であるか テストします。
名前が test で始まるメソッドは
テストであることを表します。
そしてそれらはテストランナーというツールで 自動的に実行されます。
全ての結果を見ることができます。 テストメソッドの全ての実行の
結果が集計されて
TestResult クラスのインスタンスに 集約されます。
別の例をお見せします。
この例では、名前が test で始まる テストメソッドがあります。
これがメソッド名ですが
testAdjacentRunsWithEqualsAttributesAreMerged
ここにコンテキストがあります。 オブジェクトを作ります。
ここに刺激があります。
addLast:times: メッセージを
3 回送ろうとしています。 1 回目はここにあります。
2 回目がここにあり
3 回目は 2 回目と同じです。
ここでテストします。
要素数が 2 であることを確認します。 いいですか？
同じ要素を 2 回続けて追加する ことはできませんでした。
テストを実行する上で いくつかのシナリオがありえます。
1 つ目のシナリオは「失敗」です。
つまり、表明（assert:）のどれかが
false の場合です。
そういう場合には「失敗」になります。
それは予期された問題です。
潜在的にそういうことが起こり得ると 予期しているわけです。
そして「エラー」があります。 これはチェックしようとしていたものではなく
何かが起こってしまった場合です。 テストを書いた時には
想定していなかった 例外が発生した場合などです。
これらの 2 つのものは別々のものです。
あるコードが例外を発生させることを
確認するためのテストは どう書くのでしょう？
例えば、 Set new remove: 1 をテストしたい時
このコードは NotFound 例外を 挙げるでしょう。
Set new は空集合なので
そこから要素を取り出すことは
無理です。 NotFound 例外が挙がることになります。
これをテストするには should:raise: を使います。
ブロックを渡して そのブロックの実行中に
例外が挙がるかを確認します。
この場合、例外は NotFound です。 このテストはグリーンです。OK です。
反対のことをテストすることもできます。
あるコードが例外を挙げないことを 確認します。
この場合 self shouldnt:raise: を使います。
つまり、このコードは NotFound 例外を 挙げないことを確認します。
テストを沢山書いていると
テストのコンテキストに
沢山の重複ができることがあります。
例えば ここで集合について
もう 1 つテストを書きます。 testOccurrences です。
するとコンテキストで 空集合をまた作ろうとしています。
テストを書くたびに
テストのコンテキストで Set new を書くことになります。
毎回毎回この行を繰り返したくはありません。
繰り返さないためには
それを括り出してしまえば解決します。
sUnit では setUp メソッドに
テストを実行する前の全ての初期化を 括り出します。
こうすることで
テストを実行する直前に
つまり test で始まるメソッドを 実行する直前に
setUp メソッドを実行します。
その setUp メソッドの中で コンテキストを書きます。
テストを通して刺激や
チェック、つまり表明をして
テスト実行の最後で
テストが成功であろうが失敗であろうが
tearDown メソッドを実行します。
tearDown メソッドで全ての
解放すべきリソースをきれいにします。
複数のテストメソッドの実行は簡単です。
まず setUp の実行があり
最初のテストメソッドが実行されて
tearDown を実行して片付けます。 そして新しい setUp の実行があり
新しいテストが実行され tearDown、setUp
テストの実行、そして tearDown という具合です。
こうすることでコンテキストの実装と リソースの片付けを
setUp と tearDown の 2 つのメソッドに括り出すことができます。
どんな風になるでしょうか？
SetTestCase の例では
setUp メソッドを定義して
そこで empty := Set new とします。
つまり empty は SetTestCase の インスタンス変数になります。
そしてテストメソッドの中では
すぐにインスタンス変数 empty を 使うことができます。
testOccurrence を実行する前に setUp メソッドが実行されるので
empty は既に正しく初期化されています。
sUnit のコアのクラス構成を見ると
クラスが 4 つしかありません。
1 つは TestCase です。 TestCase は単に
あるコンテキストにおいて
条件を満たすかどうかを検証するものです。 各 TestCase は
setUp メソッドと tearDown メソッドと
一連のテストメソッドを持っています。
実際に TestCase の新しいサブクラスを 書くことになります。
それらのテストケースは
TestSuite に併せられます。
そして一揃いのテストの実行を行います。
一揃いの実行をすると、結果が得られます。
その結果は TestResult のインスタンスで
何個のテストが合格して
何個のテストが実行されて
何個のテストが失敗やエラーとなったか を持っています。
TestResource というものもあります。
TestResource は TestSuite で使う リソースを定義します。
TestCaseの各サブクラス は 1 つのテストを表して
その test で始まるメソッドが 1 つのテストを表します。
TestSuite は一連のテストで
そのテストケースのテストメソッドから成ります。
TestResult は複数のテスト実行の結果で
TestResource は
一連のリソースの初期化をします。
その初期化というのは通常は
一連のテストで1度だけ初期化したい リソースの初期化です。
TestResource がリソースを 1 度初期化して 沢山のテストを実行して
そして最後に解放します。
今回学んだことは
テストの書き方です。 テストを書くのはとても単純です。
TestCase クラスのサブクラスを書いて
test で始まるメソッドを定義します。
そこにコンテキストの設定を書いて
刺激を与え、表明を検査します。
コンテキストを再利用するには
複数のテストメソッドから
setUp メソッドに括り出すことで おこないます。
まとめると、今回は sUnit テストフレームワークを見ました。
とても簡単に使うことができ
アジャイル開発を開始する上で とても効率的です。
sUnit を使うよう、強く勧めます。 テストはとても簡単に定義できます。
sUnit を使うことで 1 つのテストを作ったら
百万回それを実行できます。 どこかコードを修正した時に
コードがまだ正常に動くことを とても手軽に検査することができます。
テストを最新に保つことで
もしどこか壊れても それを即座に発見できます。
さらに他のフレームワークで テストを書くこともできます。
例えば BabyMock のような Mock フレームワークなど
異なるスタイルのテスト手法があります。
プログラムを書く時には沢山のテストを 書くことを強くお勧めします。