単体テストの基本
このトピックでは、Visual Studio テスト エクスプローラーでの単体テストの記述と実行の基本について説明します。このチュートリアルは、次のセクションで構成されています。
単体テストの概要
- クイック スタート
MyBank ソリューションの例
単体テスト プロジェクトを作成する
テストを記述する
テスト エクスプローラーでテストを実行する
テスト エクスプローラーのツール バーからテストを実行および表示する
各ビルドの後にテストを実行する
テスト リストをフィルター処理およびグループ化する
単体テストをデバッグする
単体テストのその他のツール
テストからアプリケーション コードを生成する
データ ドリブン テスト メソッドを使用して複数のテストを生成する
単体テストのコード カバレッジを分析する
Microsoft Fakes で単体テスト メソッドを分離する
単体テストの概要
Visual Studio テスト エクスプローラーは、ソフトウェアの開発手法に単体テストを組み込むチームおよび開発者をサポートするように設計されています。単体テストによって、アプリケーション コードが予測どおりに動作することを検証することで、プログラムの正確さを確認できます。単体テストでは、プログラムの機能を分析して、個々の単体としてテストできる独立したテスト可能な動作を検出します。単体テスト フレームワーク を使用して、これらの動作のテストを作成し、それらのテストの結果を報告します。
単体テストは、ソフトウェア開発ワークフローの構成要素になったときに最大の効果があります。関数またはその他のアプリケーション コードを記述したら、標準的な入力データ、境界上のデータ、および正しくないデータに対するコードの動作を検証し、コードによる明示的または暗黙的な前提を確認する単体テストをすぐに作成してください。テスト駆動開発と呼ばれるソフトウェア開発手法では、コードを記述する前に単体テストを作成するため、機能の設計ドキュメントおよび機能仕様として単体テストを使用します。
テスト エクスプローラーには、Visual Studio で単体テストを実行して結果を表示するための柔軟で効率的な方法が用意されています。Visual Studio と共に、マネージ コードおよびネイティブ コード用の Microsoft 単体テスト フレームワークがインストールされます。テスト エクスプローラーは、テスト エクスプローラーのアドオン インターフェイスを実装した、サード パーティ製やオープン ソースの単体テスト フレームワークも実行できます。Visual Studio 拡張機能マネージャーおよび Visual Studio ギャラリーを使用して、これらのフレームワークの多くを追加できます。「方法: サードパーティ製の単体テスト フレームワークをインストールする」を参照してください。
テスト エクスプローラーのビューには、すべてのテストを表示することも、成功したテスト、失敗したテスト、未実行のテスト、またはスキップされたテストだけを表示することもできます。グローバル レベルで検索ボックスでテキストを照合して、または定義済みのフィルターの 1 つを選択して、任意のビューでテストにフィルターを適用することができます。任意に選択したテストをいつでも実行できます。Visual Studio Ultimate を使用すると、ビルドの後にテストを自動的に実行できます。テスト実行の結果は、エクスプローラー ウィンドウの上部にある成功/失敗ステータス バーですぐにわかります。テストを選択すると、そのテスト メソッドの結果の詳細が表示されます。
クイック スタート
コーディングに直接関係する単体テストの概要については、次のいずれかのトピックを参照してください。
MyBank ソリューションの例
このトピックでは、例として MyBank という架空のアプリケーションの開発を使用します。このトピックの説明を読むために、実際のコードは必要ありません。テスト メソッドは C# で記述され、マネージ コード用の Microsoft 単体テスト フレームワークを使用して示されますが、その概念は他の言語やフレームワークに容易に移行できます。
MyBank アプリケーションのデザイン時の最初の計画には、個々のアカウントおよびアカウントの銀行との取引を表すアカウント コンポーネントと、個々のアカウントを集計および管理する機能を表すデータベース コンポーネントが含まれます。
次の 2 つのプロジェクトを含む MyBank ソリューションを作成します。
Accounts
BankDb
Accounts プロジェクトのデザイン時の最初の計画には、アカウントに関する基本情報を保持するクラス、アカウントでの資産の預け入れや引き出しなど、任意の種類のアカウントの共通機能を指定するインターフェイス、および当座預金アカウントを表すインターフェイスから派生したクラスが含まれます。次のソース ファイルを作成して、Accounts プロジェクトを開始します。
AccountInfo.cs は、アカウントの基本情報を定義します。
IAccount.cs は、アカウントで資産の預け入れや引き出しを行うメソッドや、アカウントの残高を取得するメソッドを含む、アカウントの標準の IAccount インターフェイスを定義します。
CheckingAccount.cs には、当座預金アカウントの CheckingAccount インターフェイスを実装する IAccounts クラスが含まれています。
当座預金アカウントからの引き出しの際は、引き出される金額がアカウントの残高より少ないことを確認する必要があります。そのため、この条件をチェックするメソッドで、IAccount.Withdaw の CheckingAccount メソッドをオーバーライドします。メソッドは次のようになります。
public void Withdraw(double amount)
{
if(m_balance >= amount)
{
m_balance -= amount;
}
else
{
throw new ArgumentException(amount, "Withdrawal exceeds balance!")
}
}
少しコードがあるので、テストしましょう。
単体テスト プロジェクトを作成する
通常、単体テスト プロジェクトは 1 つのコード プロジェクトの構造を反映します。MyBank の例で、2 つの単体テスト プロジェクト AccountsTests および BankDbTests を MyBanks ソリューションに追加します。テスト プロジェクトの名前は任意ですが、標準の名前付け規則を採用することをお勧めします。
単体テスト プロジェクトをソリューションに追加するには
[ファイル] メニューの [新規作成] をクリックし、[プロジェクト] をクリックします (キーボード: Ctrl + Shift + N)。
[新しいプロジェクト] ダイアログ ボックスで、[インストール済み] ノードを展開して、テスト プロジェクトで使用する言語を選択し、[テスト] をクリックします。
Microsoft 単体テスト フレームワークの 1 つを使用するには、プロジェクト テンプレートの一覧から [単体テスト プロジェクト] を選択します。それ以外の場合は、使用する単体テスト フレームワークのプロジェクト テンプレートを選択します。この例の Accounts プロジェクトをテストするために、プロジェクトの名前を AccountsTests に設定します。
注意 すべてのサードパーティ製およびオープン ソースの単体テスト フレームワークに、Visual Studio プロジェクトのテンプレートが用意されているわけではありません。プロジェクトの作成については、フレームワークのドキュメントを参照してください。
この例の単体テスト プロジェクトで、テスト対象のコード プロジェクトへの参照を Accounts プロジェクトに追加します。
コード プロジェクトへの参照を作成するには、
ソリューション エクスプローラーでプロジェクトを選択します。
[プロジェクト] メニューの [参照の追加] をクリックします。
[参照マネージャー] ダイアログ ボックスで、[ソリューション] ノードを開き、[プロジェクト] を選択します。コード プロジェクトの名前を選択し、ダイアログ ボックスを閉じます。
各単体テスト プロジェクトには、コード プロジェクト内のクラス名を反映したクラスが含まれています。この例では、AccountsTests プロジェクトに次のクラスが含まれています。
AccountInfoTests クラスには、AccountInfo プロジェクトの BankAccount クラス用の単体テスト メソッドが含まれています。
CheckingAccountTests クラスには、CheckingAccount クラス用の単体テスト メソッドが含まれています。
テストを記述する
使用する単体テスト フレームワークと Visual Studio IntelliSense に従って、コード プロジェクトの単体テストを作成していきます。ほとんどのフレームワークでは、テスト エクスプローラーで実行するには、単体テスト メソッドを識別する特定の属性を追加する必要があります。フレームワークには、テスト メソッドが成功したか失敗したかを示す手段も用意されています。通常は、Assert ステートメントまたはメソッドの属性を使用します。他の属性は、各テスト メソッドの前でクラスの初期化時に実行される省略可能な設定メソッド、および各テスト メソッドの後でクラスが破棄される前に実行される終了処理メソッドを識別します。
AAA (Arrange、Act、Assert) のパターンが、テスト対象のメソッドの単体テストを記述する一般的な方法です。
単体テスト メソッドの Arrange セクションでは、オブジェクトを初期化し、テスト対象のメソッドに渡されるデータの値を設定します。
Act セクションでは、設定されたパラメーターでテスト対象のメソッドを呼び出します。
Assert セクションでは、テスト対象のメソッドの操作が予測どおりに動作することを検証します。
この例の CheckingAccount.Withdraw メソッドをテストするために、2 つのテストを記述できます。メソッドの標準動作を検証するテストと、残高を超える引き出しが失敗することを検証するテストです。CheckingAccountTests クラスで、次のメソッドを追加します。
[TestMethod]
public void Withdraw_ValidAmount_ChangesBalance()
{
// arrange
double currentBalance = 10.0;
double withdrawal = 1.0;
double expected = 9.0;
var account = new CheckingAccount("JohnDoe", currentBalance);
// act
account.Withdraw(withdrawal);
double actual = account.Balance;
// assert
Assert.AreEqual(expected, actual);
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void Withdraw_AmountMoreThanBalance_Throws()
{
// arrange
var account = new CheckingAccount("John Doe", 10.0);
// act
account.Withdraw(1.0);
// assert is handled by the ExpectedException
}
Withdraw_ValidAmount_ChangesBalance は明示的な Assert ステートメントを使用してテスト メソッドの成功/失敗を判定し、Withdraw_AmountMoreThanBalance_Throws は ExpectedException 属性を使用してテスト メソッドの成功を判定していることに注意してください。表には出ませんが、単体テスト フレームワークは try/catch ステートメントでテスト メソッドをラップしています。ほとんどの場合、例外がキャッチされると、テスト メソッドは失敗し、例外は無視されます。指定された例外がスローされた場合、ExpectedException 属性によってテスト メソッドは成功します。
Microsoft 単体テスト フレームワークの詳細については、次のトピックのいずれかを参照してください。
タイムアウトを設定する
個々のテスト メソッドで、タイムアウトを設定するには
[TestMethod]
[Timeout(2000)] // Milliseconds
public void My_Test()
{ ...
}
許容される最大のタイムアウトを設定するには
[TestMethod]
[Timeout(TestTimeout.Infinite)] // Milliseconds
public void My_Test ()
{ ...
}
テスト エクスプローラーでテストを実行する
テスト プロジェクトをビルドすると、テストはテスト エクスプローラーに表示されます。テスト エクスプローラーが表示されない場合は、Visual Studio メニューの [テスト] をクリックし、[Windows]、[テスト エクスプローラー] の順に選択します。
テストを実行して、記述し、再実行すると、テスト エクスプローラーの既定のビューに [失敗したテスト]、[成功したテスト]、[スキップされたテスト]、および [テストを実行しない] グループの結果が表示されます。グループの見出しを選択して、そのグループ内のすべてのテストを表示するビューを開くことができます。
テスト エクスプローラーのツール バーからテストを実行および表示する
テスト エクスプローラーのツール バーは、対象にするテストを検出、編成、実行するのに役立ちます。
[すべて実行] を選択してテストをすべて実行することも、[実行] を選択してテストのサブセットを実行することもできます。一連のテストを実行した後、テスト実行の概要がテスト エクスプローラー ウィンドウの下部に表示されます。テストを選択すると、そのテストの詳細が下部のペインに表示されます。コンテキスト メニューから [テストを開く] を選択すると (キーボード: F12)、選択したテストのソース コードが表示されます。
各ビルドの後にテストを実行する
注意 |
---|
すべてのビルドの後の単体テスト実行は、Visual Studio Ultimate でのみサポートされます。 |
各ローカル ビルドの後で単体テストを実行するには、標準のメニューの [テスト] を選択し、テスト エクスプローラーのツール バーの [ビルド後にテストを実行] を選択します。 |
テスト リストをフィルター処理およびグループ化する
多数のテストがある場合は、テスト エクスプローラーの検索ボックスに入力し、指定した文字列によって一覧をフィルター処理できます。フィルターの一覧から選択することで、フィルター イベントをさらに制限できます。
カテゴリ別にテストをグループ化するには、[グループ化] をクリックします。 |
詳細については、「テスト エクスプローラーを使用した単体テストの実行」を参照してください。
単体テストをデバッグする
テスト エクスプローラーを使用して、テストのデバッグ セッションを開始できます。Visual Studio デバッガーを使用してコードをシームレスにステップ実行すると、テスト対象のプロジェクトと単体テストの間を切り替えることができます。デバッグを開始するには:
Visual Studio エディターで、デバッグする 1 つ以上のテスト メソッドにブレークポイントを設定します。
[!メモ]
テスト メソッドを任意の順序で実行できるため、デバッグするすべてのテスト メソッドにブレークポイントを設定します。
テスト エクスプローラーでテスト メソッドを選択し、ショートカット メニューから [選択したテストのデバッグ] を選択します。
デバッガーの詳細については、"Visual Studio でのデバッグ (Visual Studio デバッガーの新機能)" を参照してください。
単体テストのその他のツール
テストからアプリケーション コードを生成する
プロジェクト コードを記述する前にテストを記述する場合は、IntelliSense を使用してプロジェクト コードにクラスおよびメソッドを生成できます。テスト メソッドに、生成するクラスまたはメソッドを呼び出すステートメントを記述し、その呼び出しの下で IntelliSense メニューを開きます。新しいクラスのコンストラクターへの呼び出しの場合は、メニューから [新しい型の生成] を選択し、ウィザードに従ってコード プロジェクトにクラスを挿入します。メソッドへの呼び出しの場合は、IntelliSense メニューから [Generate new method (新しいメソッドの生成)] を選択します。
データ ドリブン テスト メソッドを使用して複数のテストを生成する
[!メモ]
ここに示すプロシージャは、マネージ コード用の Microsoft 単体テスト フレームワークを使用して記述したテスト メソッドにのみ適用できます。別のフレームワークを使用している場合は、同等の機能についてフレームワークのドキュメントを参照してください。
データ ドリブン テスト メソッドを使用すると、1 つの単体テスト メソッドで値の範囲を確認することができます。データ ドリブン単体テスト メソッドを作成するには、テストする変数の値を含むデータ ソースとデータ テーブルを指定する DataSource 属性でメソッドを装飾します。メソッドの本体で、TestContext.DataRow[ColumnName] インデクサーを使用して変数に行の値を割り当てます。
たとえば、CheckingAccount という名前の AddIntegerHelper クラスに不要なメソッドを追加するとします。AddIntegerHelper は 2 つの整数を追加します。
AddIntegerHelper メソッドのデータ ドリブン テストを作成するには、最初に AccountsTest.accdb という名前の Access データベースと AddIntegerHelperData という名前のテーブルを作成します。AddIntegerHelperData テーブルは、追加の 1 番目と 2 番目のオペランドを指定する列、および予期される結果を指定する列を定義します。多数の行に適切な値を入力します。
[DataSource(
@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Projects\MyBank\TestData\AccountsTest.accdb",
"AddIntegerHelperData"
)]
[TestMethod()]
public void AddIntegerHelper_DataDrivenValues_AllShouldPass()
{
var target = new CheckingAccount();
int x = Convert.ToInt32(TestContext.DataRow["FirstNumber"]);
int y = Convert.ToInt32(TestContext.DataRow["SecondNumber"]);
int expected = Convert.ToInt32(TestContext.DataRow["Sum"]);
int actual = target.AddIntegerHelper(x, y);
Assert.AreEqual(expected, actual);
}
属性付きメソッドは、テーブル内の各行に対して 1 回実行されます。イテレーションが失敗した場合、テスト エクスプローラーがメソッドのテスト失敗を報告します。メソッドのテスト結果の詳細ペインに、データの行ごとにメソッドの成功/失敗の状態が表示されます。
詳細については、「方法: データ ドリブン単体テストを作成する」を参照してください。
単体テストのコード カバレッジを分析する
[!メモ]
単体テストのコード カバレッジは、単体テスト フレームワークによって実行できるネイティブ言語とマネージ言語、およびすべての単体テスト フレームワークで使用できます。
Visual Studio のコード カバレッジ ツールを使用して、単体テストで実際にテスト中の製品コードの量を確認できます。選択したテストまたはソリューションのすべてのテストのコード カバレッジを実行できます。[コード カバレッジの結果] ウィンドウに、行、関数、クラス、名前空間、およびモジュールで実行された製品コードのブロックのパーセンテージが表示されます。
ソリューションのテスト メソッドのコード カバレッジを実行するには、
Visual Studio メニューの [テスト] を選択し、[コード カバレッジの分析] を選択します。
次のいずれかのコマンドを選択します。
[選択されたテスト]: テスト エクスプローラーで選択したテスト メソッドを実行します。
[All tests (すべてのテスト)]: ソリューション内のすべてのテスト メソッドを実行します。
カバレッジの結果は、[コード カバレッジの結果] ウィンドウに表示されます。
詳細については、「コード カバレッジを使用した、テストされるプロジェクトのコード割合の確認」を参照してください。
Microsoft Fakes で単体テスト メソッドを分離する
[!メモ]
Microsoft Fakes は、Visual Studio Ultimate でのみ使用できます。Microsoft Fakes は、マネージ コード用の単体テスト フレームワークを使用して記述したテスト メソッドでのみ使用できます。
問題
関数の内部コードの検証に集中する単体テスト メソッドは、テスト対象のメソッドが外部依存関係を追加する関数を呼び出す場合、作成が難しくなります。たとえば、例の CheckingAccount クラスのメソッドは、メイン データベースを更新するために BankDb コンポーネントへの呼び出しを行うと考えられます。CheckingAccount クラスを次のようにリファクタリングします。
class CheckingAccount : IAccount
{
public CheckingAccount(customerName, double startingBalance, IBankDb bankDb)
{
m_bankDb = bankDb;
// set up account
}
public void Withdraw(double amount)
{
if(m_balance >= amount)
{
m_balance = m_MyBankDb.Withdraw(m_accountInfo.ID, amount);
}
else
{
throw new ArgumentException(amount, "Withdrawal exceeds balance!")
}
}
private IBankDb m_bankDb = null;
// ...
この CheckingAccount.Withdraw メソッドの単体テストは、m_bankDb.Withdraw への呼び出しに起因する問題で失敗する場合があります。データベースまたはネットワーク接続が失われることも、データベースのアクセス許可が間違っていることもあります。m_bankDB.Withdraw の呼び出しの失敗によって、内部コードに関係のない理由でテストが失敗します。
Microsoft Fakes ソリューション
Microsoft Fakes は、依存関係を生じる単体テスト メソッド内のクラスと置き換えることができるクラスおよびメソッドを含むアセンブリを作成します。生成された Fakes モジュール内の代替クラスは、対象コンポーネント内の各パブリック メソッドのメソッドとデリゲートを宣言します。テスト メソッドで、テストするメソッド内の依存関係の呼び出しの正確な動作を作成するためのデリゲートを実装します。
この例では、BankDb プロジェクト用の Fakes アセンブリを作成し、Fakes によって生成されて StubIBankDb インターフェイスから派生した IBankDb クラスを使用して、データベースとのやり取りに起因する不確実性を除去することができます。変更後の Withdraw_ValidAmount_ChangesBalance テスト メソッドは次のようになります。
[TestMethod]
public void Withdraw_ValidAmount_ChangesBalance()
{
// arrange
double currentBalance = 10.0;
double withdrawal = 1.0;
double expected = 9.0;
// set up the Fakes object and delegate
var stubBankDb = new MyBank.Stubs.StubIBankDb();
stubBankDb.WithdrawDoubleDouble = (id, amount) => { return 9.0; }
var account = new CheckingAccount("JohnDoe", currentBalance, stubBankDb);
// act
account.Withdraw(withdrawal);
double actual = account.Balance;
// assert
Assert.AreEqual(expected, actual);
}
テスト メソッド内の行
stubBankDb.WithdrawDoubleDouble = (id, amount) => { return 9.0; }
で、ラムダ式を使用して Withdraw メソッドの Fakes デリゲートを実装します。stubBankDb.Withdraw メソッドはデリゲートを呼び出し、常に指定された金額を返します。それによって、テスト メソッドは高い信頼性で Accounts メソッドの動作を検証することができます。
Microsoft Fakes の追加情報
Microsoft Fakes は、次の 2 つの方法で代替クラスを作成します。
スタブは、対象の依存関係クラスの親インターフェイスから派生した代替クラスを生成します。スタブ メソッドは、対象クラスのパブリック仮想メソッドの代わりに使用できます。
Shim は、ランタイム インストルメンテーションを使用して、対象メソッドへの呼び出しを仮想でないメソッドの代替 shim メソッドに転換します。
いずれの方法でも、依存関係メソッドへの呼び出しの生成されたデリゲートを使用して、テスト メソッド内の動作を指定します。
詳細については、「Microsoft Fakes を使用したテストでのコードの分離」を参照してください。