VS 2008 Pro版に搭載されている単体テスト機能を使ってテスト・ファーストを実践。NUnitからのテスト移行についても考察。
本稿の前編では、Visual Studio 2008 Professional Edition(以下、VS 2008)に搭載されるようになった単体テスト機能の機能項目を網羅的に解説した。
後編となる今回では、この単体テスト機能を使ったテスト駆動開発の手順について紹介する。また後半では、NUnitからのテスト・コードの移行の手順についても解説する。
前編ではあまり触れなかったが、単体テストはアジャイル開発における「テストファースト」や「テスト駆動開発」と呼ばれる開発手法の普及に合わせて大きく注目されるようになった。
テスト駆動開発では、最初に(システム本体ではなく)テスト・コードを記述する。テスト・コードの記述がすべて終わると、次にテストが失敗するように本体のコードを実装する。ここで1度自動テストを実施して、実際にテストが失敗するのを確認する。
そして次に、テストが成功するようにコードを実装して、テストを実施する。きちんと仕様どおりに動くコードが記述できたら、次にコードをきれいにするためにリファクタリングを実施する。リファクタリングが終わると、修正後のコードが正しく動作することを確認するために再度テストを実施する。
ここまでの流れをテスト駆動開発では、「レッド/グリーン/リファクタリング」と呼ぶ。多くのテスト・ツールでは、テストの失敗がレッドで、テストをすべてパスした状態がグリーンで示されるためだ。
では、VS 2008を使って実際にテスト駆動開発を行ってみよう。
■1. 仕様確認
テスト駆動開発は、単純にテストを先に記述することではなく、テスト・コードの記述により、あらかじめ検討した仕様の実装も検討する。
ここでは商品(Goods)クラスを作成して、購入数で割引率の異なる商品単価を取得するGetUnitPriceメソッドを作っていく。商品単価は10個以上購入で5%割引、30個以上で10%割引、50個以上で15%割引とする。また、単価の端数は切り捨てるものとする。
■2. テスト・コードの作成 〜 テストの実施(レッド)
最初に行うのはテスト・コードの作成だが、テスト対象となるクラスをあらかじめ作っておくとテスト・コードの作成が楽である。ここでは、まずクラス・ライブラリを作っておく。
これには、テンプレートとして「クラス ライブラリ」を使用したプロジェクトを作成する。「Class1.cs」が作成されるので、ファイル名を「Goods.cs」に変更しておこう。確認ダイアログで、[Yes]をクリックすると、ソース・コード内の「Class1」も、「Goods」に変更される。
次にコード・エディタ上でクラスを右クリックして、単体テスト・プロジェクトを作成する。以下にその手順を示す。
クラス内で右クリックして[単体テストの作成]を実行
テスト用のプロジェクトの名前を入力
以上により、テスト・クラスが表示され、(Goodsクラスの)コンストラクタ用のテスト・コードが作成される。コンストラクタのテストが必要ならここに記述すればよいし、必要なければメソッドを削除すればよい。
次に、単体テストのコードを記述していく。テスト・メソッドを作成して、コードを記述するが、この時点では本体のコード(実装予定のGetUnitPriceメソッド)を実装していないため、当然ながらIntelliSenseにはそのメソッドは表示されない。
テスト・コードを書き上げると、存在していないメソッドが赤字で表示される。そこにカーソルを移動し、修正候補部分をクリックすると「メソッド・スタブ」を生成できる。
メソッド・スタブの生成を実行すると、Goodsクラス側にGetUnitPriceメソッドが生成される。
public int GetUnitPrice(int p)
{
throw new NotImplementedException();
}
ここまでできたら、単体テスト・メソッドを作成していく。ここでは、次のような境界値のテストを作成していく(単純化のため、割引なしの商品単価は120円としている)。
[TestMethod()]
public void GetUnitPriceTest9()
{
Goods goodsInstance = new Goods();
int actual = goodsInstance.GetUnitPrice(9);
Assert.AreEqual<int>(120, actual, "9個では割引がない");
}
[TestMethod()]
public void GetUnitPriceTest10()
{
Goods goodsInstance = new Goods();
int actual = goodsInstance.GetUnitPrice(10);
Assert.AreEqual<int>(System.Convert.ToInt32(120 * 0.95),
actual, "10個の購入で5%割引");
}
[TestMethod()]
public void GetUnitPriceTest29()
{
Goods goodsInstance = new Goods();
int actual = goodsInstance.GetUnitPrice(29);
Assert.AreEqual<int>(System.Convert.ToInt32(120 * 0.95),
actual,"29個では、5%割引");
}
[TestMethod()]
public void GetUnitPriceTest30()
{
Goods goodsInstance = new Goods();
int actual = goodsInstance.GetUnitPrice(30);
Assert.AreEqual<int>(System.Convert.ToInt32(120 * 0.90),
actual, "30個では、10%割引");
}
[TestMethod()]
public void GetUnitPriceTest49()
{
Goods goodsInstance = new Goods();
int actual = goodsInstance.GetUnitPrice(49);
Assert.AreEqual<int>(System.Convert.ToInt32(120 * 0.90),
actual, "49個では、10%割引");
}
[TestMethod()]
public void GetUnitPriceTest50()
{
Goods goodsInstance = new Goods();
int actual = goodsInstance.GetUnitPrice(50);
Assert.AreEqual<int>(System.Convert.ToInt32(120 * 0.85),
actual, "50個では、15%割引");
}
テスト・コードを作成するときに意識すべきことは次の点だ。
仕様にないことまで確認するテスト・コードを記述する必要はない。逆に、テストする必要があるのであれば、仕様を追加する必要がある。つまり、最初に挙げた仕様を確認するために最低限必要なテストのみをここに記述したことになる。
さて、それではテストを実施してみよう。本体側の実装が終わっていないので、当然すべてのテストは失敗する。
これで「テストの実施(レッド)」が完了したことになる。
■3. コードの作成 〜 テストの実施(グリーン)
さて、レッドの段階が完了すると、次に本体のコードの作成に入ることになる。コードの実装は通常のコーディングとまったく変わらない。仕様に従って素直に実装したのが以下のコードである。
public int GetUnitPrice(int quantity)
{
// 50個以上で15%割引
if (quantity >= 50)
{
return Convert.ToInt32(120 * 0.85);
}
// 30個以上で10%割引
if (quantity >= 30)
{
return Convert.ToInt32(120 * 0.90);
}
// 10個以上で5%割引
if (quantity >= 10)
{
return Convert.ToInt32(120 * 0.95);
}
return 120;
}
この状態で再度テストを実施してみよう。
これですべてのテストが成功したグリーンの状態になった。
■4. リファクタリング 〜 テストの実施(グリーン)
すべての仕様を満たし、テストがグリーンの状態になったら次はリファクタリングである。
リファクタリングとは、動作はそのままでソース・コードを“きれい”にする作業である。テスト駆動開発では、「重複したコードを排除する」ことが1つの大きな目標として挙げられている。
C#には(開発言語としてC#を選択してVS 2008のプロジェクトを作成した場合)、リファクタリングを行うための「リファクタ機能」が用意されていて、コード・エディタ上の右クリック・メニューから実行することができる。
Visual Basicには、リファクタ機能が用意されていないが、Refactor! for Visual Basic 2008という製品が無料で提供されている。これをインストールすれば、C#と同じように右クリック・メニューで実行することができるが、こちらは実行可能なメニューのみが表示される。
リファクタリングが完了すると、テストがすべて正しく動作することを再度確認する。すでにテストはパスしているので、リファクタリングによるデグレード(新たなバグの作り込み)はすぐに判明する。
以上で簡単ではあるが、テスト駆動開発の一連の流れを見てきた。この後は、機能が追加されるたびにテスト・コードの作成→確認→実装という、同じ流れをたどることになる。
Copyright© Digital Advantage Corp. All Rights Reserved.