UMLの開始/終了の記号が覚えられない
いままで何度となく分からなくなってその都度調べていているのが、UMLの状態遷移図やアクティビティ図における開始記号(黒丸:●)と終了記号(二重黒丸:◎の中が塗りつぶし)です。
コード実行のカバレッジ(網羅)の分類
テストを実行して、テスト対象のコードのどの部分が実行されたかを計測する際、命令網羅、分岐網羅、条件網羅、パス網羅といった言葉や、C0、C1、C2、といった略語を目にします。
先日JavaのモックツールJMockitに搭載されるカバレッジ計測機能を調べた時に、JMockitは、"Line coverage"と"Path coverage"を計測するとありました。LineとPathがどの網羅に該当するかを調べてみました。
- 命令網羅(C0)
- 全命令(命令語)を1回以上実行
- 分岐網羅(C1)
- 「条件網羅」ということもある。条件分岐で生じる経路がすべて実行される(条件文の真と偽の分岐が実行される)
- 複合条件網羅(C2)
- 「パス網羅」ということもある。条件分岐のすべての組み合わせが実行される
これだけだと、知っている人にしか分からない状態なので、例を挙げて考えてみます。
/** * 商品の課金額を算出する。 * 価格が1500円未満の商品は送料を500円加える。 * 会員資格がGOLDの場合、商品価格を10%割引とする。 */ public int charge(int price, int rank) { int shipping = 0; if (price < 1500) { shipping = 500; } if (rank == Member.GOLD) { price *= 0.9; } return price + shipping; }
このコードにおいて、各網羅を100%とするテストケースの数は、
- 命令網羅(C0)が100%となる場合のテストケースは1通り
- 入力:price = 500, rank = Member.GOLD
- 分岐網羅(C1)が100%となる場合のテストケースは2通り
- 入力:price = 500, rank = Member.NORMAL
- 入力:price = 2000, rank = Member.GOLD
- 複合条件網羅(C2)が100%となる場合のテストケースは4通り
- 入力:price = 500, rank = Member.NORMAL
- 入力:price = 500, rank = Member.GOLD
- 入力:price = 2000, rank = Member.NORMAL
- 入力:price = 2000, rank = Member.GOLD
となります。
なお、この複合条件網羅(C2)に必要なテストケース数が、McCabeの循環複雑度と一致するらしく(未確認)、メソッドをシンプルに作ることがテスト工数を下げることにつながるというお話です。
ここで、当初のJMockitのカバレッジですが、Lineは想定通り命令網羅(C1)、Pathは複合条件網羅(C2)でした。
単体試験(単体テスト、ユニットテスト)のやり方いろいろ
ここでは、単体試験対象はメソッド・関数レベルとします(時々、単体試験というと、1本のプログラムとか1機能を対象にするという人がいるので、定義を明示しておきます)。単体試験をどのような環境で実施するのか、いくつか流儀があるようです。
デバッガ上で対象メソッドを実行する
聞いた話では、デバッガ上で対象コードを実行し、デバッガ上で変数の値を変更し、ステップ実行(かブレークポイントを置いて実行するか)させて、デバッガ上で挙動を見て試験結果を記載するというもののようです。デバッガ上で値の確認をすることから結果判定は人間系となってしまうようです。
コンパイルを通して実行可能とするためには、テストスタブが必要なはずですが、そこまで深く話を聞いたことがないのでどうやっているか不明です。
単体試験でのスタブとドライバの作成
- スタブは作らずコメントアウト
- スタブは作らず本物を使用(それって結合試験だよね)
- スタブをまじめに作ります
- スタブはモックツールで簡単に済ませます
などいろいろあるようです。
スタブを作るとなると、数が莫大になるので、コストが通常の3倍、などと言われることがあります。
スタブを作らない場合で、本物が揃ってからテストするという場合、それって結合試験だよね、という話と、コードを書いたその場で単体テストをしていないので、コード作成時点とテスト実施時点が離れてしまい、開発効率の低下が気になります。
なので、理想はモックツールでスタブを作らずに済ませる方向だと考えています。
テスト代替えの種類
Microsoftのサイトに、テスト代替の種類が載っていたので、URLをメモ。
http://msdn.microsoft.com/ja-jp/magazine/cc163358.aspx
テストファーストは単体試験ではない
ただし、テストファーストで作成することによって、単体試験が容易になります。
TestNGかJUnitか
調べれば調べるほどTestNGに傾いています。特にカバレッジの網羅を狙うと、テストメソッドは1つで、複数の入力・期待値のデータの組を用意して、これを流せる機能がほしくなります。JUnit 4でもParameterizedクラスと@Parametersを使って実現できなくはないですが、Parameterizedを使うと、そのテストクラスに定義したメソッドすべてがデータの組の数と等しい回数呼び出されてしまい、いま三ついけてません。
などと課題はありますが、デファクトスタンダードの強み、情報の豊富さを凌駕できるかというと、そこまでではないかなぁと思ってしまいます。ツールの性能の違いが、生産性の決定的差でないということです。
んっ、Theoriesクラスがもしかすると使える?
追加メモ
- よい単体テストの特徴と、書くためのヒント
http://builder.japan.zdnet.com/news/story/0,3800079086,20410767,00.htm
ダミー、スタブ、スパイ、フェイク、モックについて記載があります。
単体テストで、テスト対象が依存するコンポーネントの代替えについて振る舞いで分類していました。