6-1. DataTestMethod/DataRow(パラメタライズドテスト)
これは待ち望まれていた機能です。この機能の有無が、従来のデスクトップアプリ用のユニットテストで、MSTestではなくNUnitを選ぶ大きな理由になっていました。それがストアアプリ用のMSTestで、ようやく提供されたのです(前述したように、この機能は従来のデスクトップアプリ用のMSTestではまだ使えません)。
パラメタライズドテストには、DataTestMethod属性とDataRow属性を使います。ここでは、共有プロジェクトにFizzBuzzを実装してみましょう。
FizzBuzzの実装手順は、従来のデスクトップアプリ用のMSTestならば「FizzBuzzを作ってみる」(PDF)のようになります(FizzBuzzをご存知ない方はこのPDFをご覧ください)。
DataTestMethod属性とDataRow属性を使うと、テストコードは次のように書けます(最終形だけを示します)。このテストコードは、テストの共有プロジェクトに「FizzBuzzPlayerTest.cs」ファイルを作って、そこに記述します。
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
namespace CsTdd08Tests.Shared
{
[TestClass]
public class FizzBuzzPlayerTest
{
[DataTestMethod]
[DataRow(1, "1", DisplayName = "1のときは「1」")]
[DataRow(2, "2", DisplayName = "2のときは「2」")]
[DataRow(3, "Fizz", DisplayName = "3のときは「Fizz」")]
[DataRow(5, "Buzz", DisplayName = "5のときは「Buzz」")]
[DataRow(15, "Fizz Buzz", DisplayName = "15のときは「Fizz Buzz」")]
public void TestSay(int n, string expected)
{
CsTdd08App.FizzBuzzPlayer p = new CsTdd08App.FizzBuzzPlayer();
Assert.AreEqual<string>(expected, p.Say(n));
}
}
}
完成した製品コードは次のようになります。共有プロジェクトの「FizzBuzzPlayer.cs」ファイルです。
FizzBuzzPlayerクラス
namespace CsTdd08App
{
public class FizzBuzzPlayer
{
public string Say(int number)
{
bool isMultipleOf3 = (number % 3 == 0);
bool isMultipleOf5 = (number % 5 == 0);
if (isMultipleOf3 && isMultipleOf5)
return "Fizz Buzz";
if (isMultipleOf3)
return "Fizz";
if (isMultipleOf5)
return "Buzz";
return number.ToString();
}
}
}
なお、テストエクスプローラーでは、上述のTestSayテストは1つしか表示されませんが、それを選択すると下のペインにテスト名とともに複数の結果が表示されます(次の画像)。このテスト名は、DataRow属性に与えたDisplayName名前付き引数です。
ここで紹介したパラメタライズドテストの機能は、ストアアプリ専用です。従来のデスクトップアプリ用のMSTestでは利用できません。従来のデスクトップアプリ用のユニットテストでは、MSTestで「Chaining Assertion for MSTest」を利用したり(『C#で始めるテスト駆動開発入門(2):Visual StudioのMSTestでTDDを行う方法』)、NUnitなどの他のユニットテスティングフレームワークを採用するなどしていました。
#ifディレクティブの利用
ここで、共有しているユニットテストのコードでも、#ifディレクティブを使ってWindowsとPhoneのコードを切り分けられることを確かめておきましょう。
Phone用のユニットテストプロジェクトには、WINDOWS_PHONE_APPコンパイル定数がすでに定義されています。Windows用のユニットテストプロジェクトには定義されていないので、プロジェクトのプロパティを開き、左の[ビルド]タブを選び、[条件付きコンパイルシンボル]の欄の末尾に「;WINDOWS_APP」と追加します。
そうしたら、Phone用のFizzBuzzの仕様を変えることにしましょう。Phoneの画面が狭いことを考慮して、3と5の倍数のときは「Fizz Buzz」ではなく、スペースを削った「FizzBuzz」を返すことにします。すると、#ifディレクティブを使って、テストコードは次のように書けます。
#ifディレクティブを使ってテストを切り分ける
[DataTestMethod]
[DataRow(1, "1", DisplayName = "1のときは「1」")]
[DataRow(2, "2", DisplayName = "2のときは「2」")]
[DataRow(3, "Fizz", DisplayName = "3のときは「Fizz」")]
[DataRow(5, "Buzz", DisplayName = "5のときは「Buzz」")]
#if WINDOWS_APP
[DataRow(15, "Fizz Buzz", DisplayName = "15のときは「Fizz Buzz」")]
#endif
#if WINDOWS_PHONE_APP
[DataRow(15, "FizzBuzz", DisplayName = "15のときは「FizzBuzz」")]
#endif
public void TestSay(int n, string expected)
{
CsTdd08App.FizzBuzzPlayer p = new CsTdd08App.FizzBuzzPlayer();
Assert.AreEqual<string>(expected, p.Say(n));
}
これでテストを走らせてみると、Phone用のテストだけがRED(失敗)になります(次の画像)。
製品コードも#ifディレクティブを使って、次のように修正します。これで再びALL GREENになります。
Sayメソッド
public string Say(int number)
{
bool isMultipleOf3 = (number % 3 == 0);
bool isMultipleOf5 = (number % 5 == 0);
if (isMultipleOf3 && isMultipleOf5)
#if WINDOWS_APP
return "Fizz Buzz";
#endif
#if WINDOWS_PHONE_APP
return "FizzBuzz";
#endif
if (isMultipleOf3)
return "Fizz";
if (isMultipleOf5)
return "Buzz";
return number.ToString();
}


