チュートリアル: マネージ コードの単体テストを作成し、実行するWalkthrough: Create and Run Unit Tests for Managed Code

このチュートリアルでは、マネージド コード用の Microsoft 単体テスト フレームワークと Visual Studio テスト エクスプローラーを使用して一連の単体テストを作成、実行、およびカスタマイズする手順について説明します。This walkthrough steps you through creating, running, and customizing a series of unit tests using the Microsoft unit test framework for managed code and the Visual Studio Test Explorer. 開発中の C# プロジェクトで作業を開始し、そのコードを実行するテストを作成し、テストを実行し、結果を調べます。You start with a C# project that is under development, create tests that exercise its code, run the tests, and examine the results. 次に、プロジェクト コードを変更し、テストを再実行します。Then you can change your project code and rerun the tests.

注意

このチュートリアルでは、マネージ コード用の Microsoft 単体テスト フレームワークを使用します。This walkthrough uses the Microsoft unit test framework for managed code. また、テスト エクスプローラー用のアダプターを備えたサード パーティの単体テスト フレームワークからテスト エクスプローラーを実行することもできます。Test Explorer also can run tests from third party unit test frameworks that have adapters for Test Explorer. 詳細については、「サードパーティ製の単体テスト フレームワークをインストールする」をご覧ください。For more information, see Install third-party unit test frameworks

注意

コマンド ラインからテストを実行する方法については、「チュートリアル: コマンド ライン テスト ユーティリティの使用」を参照してください。For information about how to run tests from a command line, see Walkthrough: Use the command-line test utility.

必須コンポーネントPrerequisites

テストするプロジェクトを作成するCreate a project to test

  1. Visual Studio を開きます。Open Visual Studio.

  2. [ファイル] メニューで、[新規作成] > [プロジェクト] の順に選択します。On the File menu, select New > Project.

    [新しいプロジェクト] ダイアログ ボックスが表示されます。The New Project dialog box appears.

  3. [インストールされているテンプレート][Visual C#] をクリックします。Under Installed Templates, click Visual C#.

  4. アプリケーションの種類の一覧の [クラス ライブラリ] をクリックします。In the list of application types, click Class Library.

  5. In the [名前] ボックスに「 Bank をポイントし、 [OK]」をご覧ください。In the Name box, type Bank and then click OK.

    注意

    "Bank" という名前が既に使用されている場合は、別のプロジェクト名を選択します。If the name "Bank" is already used, choose another name for the project.

    新しい Bank プロジェクトが作成され、コード エディターに Class1.cs ファイルが開いた状態でソリューション エクスプローラーが表示されます。The new Bank project is created and displayed in Solution Explorer with the Class1.cs file open in the code editor.

    注意

    コード エディターに Class1.cs ファイルが表示されない場合、ソリューション エクスプローラーのファイル Class1.cs をダブルクリックして開きます。If the Class1.cs file is not open in the Code Editor, double-click the file Class1.cs in Solution Explorer to open it.

  6. 単体テストを作成するサンプル プロジェクト」からソース コードをコピーし、Class1.cs の元の内容をコピーしたコードに置き換えます。Copy the source code from the Sample Project for Creating Unit Tests, and replace the original contents of Class1.cs with the copied code.

  7. ファイルを BankAccount.cs という名前で保存します。Save the file as BankAccount.cs.

  8. [ビルド] メニューの [ソリューションのビルド] をクリックします。On the Build menu, click Build Solution.

Bank という名前のプロジェクトができます。You now have a project named Bank. これには、テストするソース コードとテストに使用するツールが含まれています。It contains source code to test and tools to test it with. Bank の BankAccountNS 名前空間には、パブリック クラス BankAccount が含まれます。そのメソッドを次の手順でテストします。The namespace for Bank, BankAccountNS, contains the public class BankAccount, whose methods you'll test in the following procedures.

この記事のテストは Debit メソッドに焦点を当てています。In this article, the tests focus on the Debit method. Debit メソッドは、口座から現金が引き出されるときに呼び出されます。The Debit method is called when money is withdrawn from an account. メソッド定義は次のとおりです。Here is the method definition:

// Method to be tested.
public void Debit(double amount)
{
    if(amount > m_balance)
    {
        throw new ArgumentOutOfRangeException("amount");
    }
    if (amount < 0)
    {
        throw new ArgumentOutOfRangeException("amount");
    }
    m_balance += amount;
}

単体テスト プロジェクトを作成するCreate a unit test project

  1. [ファイル] メニューで [追加] > [新しいプロジェクト] の順に選択します。On the File menu, select Add > New Project.

  2. [新しいプロジェクト] ダイアログ ボックスで、 [インストール済み][Visual C#] の順に展開し、 [テスト] をクリックします。In the New Project dialog box, expand Installed, expand Visual C#, and then choose Test.

  3. テンプレートの一覧から、 [単体テスト プロジェクト] を選択します。From the list of templates, select Unit Test Project.

  4. [名前] ボックスに「BankTests」と入力して、[OK] を選択します。In the Name box, enter BankTests, and then select OK.

    BankTests プロジェクトが Bank ソリューションに追加されます。The BankTests project is added to the Bank solution.

  5. BankTests プロジェクトで、Bank プロジェクトへの参照を追加します。In the BankTests project, add a reference to the Bank project.

    ソリューション エクスプローラーで、BankTests プロジェクトの [参照設定] をクリックし、コンテキスト メニューの [参照の追加] をクリックします。In Solution Explorer, select References in the BankTests project and then choose Add Reference from the context menu.

  6. [参照マネージャー] ダイアログ ボックスで、 [ソリューション] を展開し、 [Bank] チェックボックスをオンにします。In the Reference Manager dialog box, expand Solution and then check the Bank item.

テスト クラスを作成するCreate the test class

BankAccount クラスを検証するテスト クラスを作成します。Create a test class to verify the BankAccount class. プロジェクト テンプレートによって生成された UnitTest1.cs ファイルを使用できますが、ファイルとクラスにはよりわかりやすい名前を付けます。You can use the UnitTest1.cs file that was generated by the project template, but give the file and class more descriptive names. ソリューション エクスプローラーでファイルの名前変更機能を使用すると、この処理を 1 つの手順で実行できます。You can do that in one step by renaming the file in Solution Explorer.

クラス ファイルの名前を変更するRename a class file

ソリューション エクスプローラーで、BankTests プロジェクトの UnitTest1.cs ファイルを選択します。In Solution Explorer, select the UnitTest1.cs file in the BankTests project. コンテキスト メニューの [名前の変更] をクリックし、ファイルの名前を BankAccountTests.cs に変更します。From the context menu, choose Rename, and then rename the file to BankAccountTests.cs. プロジェクト内のコード要素 UnitTest1 に対するすべての参照を名前変更するかどうかを確認するダイアログで、[はい] をクリックします。Choose Yes on the dialog that asks if you want to rename all references to the code element UnitTest1 in the project.

この手順により、クラスの名前が BankAccountTestsに変更されます。This step changes the name of the class to BankAccountTests. BankAccountTests.cs ファイルには次のコードが含まれるようになりました。The BankAccountTests.cs file now contains the following code:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace BankTests
{
    [TestClass]
    public class BankAccountTests
    {
        [TestMethod]
        public void TestMethod1()
        {
        }
    }
}

テスト対象のプロジェクトに using ステートメントを追加するAdd a using statement to the project under test

using ステートメントをクラスに追加して、完全修飾名を使用せずにテスト対象のプロジェクトに呼び出すことができるようにすることもできます。You can also add a using statement to the class to be able to call into the project under test without using fully qualified names. クラス ファイルの先頭に次のように追加します。At the top of the class file, add:

using BankAccountNS;

テスト クラスの要件Test class requirements

テスト クラスの最小要件は次のとおりです。The minimum requirements for a test class are:

  • マネージ コード用の Microsoft 単体テスト フレームワークでは、テスト エクスプローラーで実行する単体テスト メソッドを含むすべてのクラスについて、 [TestClass] 属性が必要です。The [TestClass] attribute is required in the Microsoft unit testing framework for managed code for any class that contains unit test methods that you want to run in Test Explorer.

  • テスト エクスプローラーで実行する各テスト メソッドには [TestMethod]属性が必要です。Each test method that you want Test Explorer to run must have the [TestMethod]attribute.

単体テスト プロジェクトで [TestClass] 属性がない別のクラスを使用することができます。また、テスト クラスで [TestMethod] 属性がない別のメソッドを使用することもできます。You can have other classes in a unit test project that do not have the [TestClass] attribute, and you can have other methods in test classes that do not have the [TestMethod] attribute. こうした別のクラスやメソッドをテスト メソッドで使用できます。You can use these other classes and methods in your test methods.

最初のテスト メソッドを作成するCreate the first test method

この手順では、BankAccount クラスの Debit メソッドの動作を検証する単体テスト メソッドを記述します。In this procedure, you'll write unit test methods to verify the behavior of the Debit method of the BankAccount class. Debit メソッドについては、この記事で前述されています。The Debit method is shown previously in this article.

確認する必要がある動作は少なくとも 3 つあります。There are at least three behaviors that need to be checked:

  • 引き落とし金額が残高を上回る場合、このメソッドは ArgumentOutOfRangeException をスローします。The method throws an ArgumentOutOfRangeException if the debit amount is greater than the balance.

  • 引き落とし金額が 0 未満の場合、このメソッドは ArgumentOutOfRangeException をスローします。The method throws ArgumentOutOfRangeException if the debit amount is less than zero.

  • 引き落とし金額が有効な場合、このメソッドは口座残高から借方金額を減算します。If the debit amount is valid, the method subtracts the debit amount from the account balance.

ヒント

このチュートリアルでは使用しないため、既定の TestMethod1 メソッドを削除できます。You can delete the default TestMethod1 method, because you won't use it in this walkthrough.

テスト メソッドを作成するにはTo create a test method

最初のテストでは、正しい金額 (つまり、口座残高未満かつ 0 を上回る金額) によって口座から正しい金額が引き出されることが確認されます。The first test verifies that a valid amount (that is, one that is less than the account balance and greater than zero) withdraws the correct amount from the account. 次のメソッドを BankAccountTests クラスに追加します。Add the following method to that BankAccountTests class:

[TestMethod]
public void Debit_WithValidAmount_UpdatesBalance()
{
    // Arrange
    double beginningBalance = 11.99;
    double debitAmount = 4.55;
    double expected = 7.44;
    BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);

    // Act
    account.Debit(debitAmount);

    // Assert
    double actual = account.Balance;
    Assert.AreEqual(expected, actual, 0.001, "Account not debited correctly");
}

このメソッドは単純です。期首残高を含む新しい BankAccount オブジェクトを設定し、有効な金額を引き出します。The method is straightforward: it sets up a new BankAccount object with a beginning balance, and then withdraws a valid amount. 期末残高が予想どおりであることを確認するには、AreEqual メソッドを使用します。It uses the AreEqual method to verify that the ending balance is as expected.

テスト メソッドの要件Test method requirements

テスト メソッドは次の条件を満たしている必要があります。A test method must meet the following requirements:

  • [TestMethod] 属性で修飾されています。It's decorated with the [TestMethod] attribute.

  • void を返します。It returns void.

  • パラメーターを含むことができません。It cannot have parameters.

テストをビルドして実行するBuild and run the test

  1. [ビルド] メニューの [ソリューションのビルド] をクリックします。On the Build menu, choose Build Solution.

    エラーがない場合は、テスト エクスプローラーが表示され、[テストを実行しない] グループに [Debit_WithValidAmount_UpdatesBalance] が表示されます。If there are no errors, Test Explorer appears with Debit_WithValidAmount_UpdatesBalance listed in the Not Run Tests group.

    ヒント

    ビルドの成功後にテスト エクスプローラーが表示されない場合は、メニューの [テスト] をクリックし、[ウィンドウ][テスト エクスプローラー] の順に選択します。If Test Explorer does not appear after a successful build, choose Test on the menu, then choose Windows, and then choose Test Explorer.

  2. [すべて実行] をクリックしてテストを実行します。Choose Run All to run the test. テストの実行中は、ウィンドウの上部にあるステータス バーがアニメーション化されます。While the test is running, the status bar at the top of the window is animated. テストの実行の終了時に、すべてのテスト メソッドが成功した場合はステータス バーが緑色に変わり、いずれかのテストが失敗した場合は赤色に変わります。At the end of the test run, the bar turns green if all the test methods pass, or red if any of the tests fail.

  3. この場合は、テストが失敗します。In this case, the test fails. このテスト メソッドは、[失敗したテスト] グループに移動します。The test method is moved to the Failed Tests group. テスト エクスプローラーでメソッドを選択すると、ウィンドウの下部に詳細が表示されます。Select the method in Test Explorer to view the details at the bottom of the window.

コードを修正してテストを再実行するFix your code and rerun your tests

テスト結果を分析するAnalyze the test results

テスト結果には失敗を示すメッセージが含まれています。The test result contains a message that describes the failure. AreEquals メソッドについて、メッセージには、想定された事項 (Expected<value> パラメーター) および実際に受け取られた事項 (Actual<value> パラメーター) が示されます。For the AreEquals method, the message displays what was expected (the Expected<value> parameter) and what was actually received (the Actual<value> parameter). 残高が減少すると予想していましたが、実際には代わりに引き出し額によって増加しています。You expected the balance to decrease, but instead it actually increased by the amount of the withdrawal.

単体テストにより、引き出し額は、減算する必要があるときに口座残高に追加されます。単体テストではバグがわかりました。The unit test has uncovered a bug: the amount of the withdrawal is added to the account balance when it should be subtracted.

バグを修正するCorrect the bug

エラーを修正するには、行を置き換えます。To correct the error, replace the line:

m_balance += amount;

次の内容に置き換えます。with:

m_balance -= amount;

テストを再実行するRerun the test

テスト エクスプローラーで、 [すべて実行] をクリックしてテストを再実行します。In Test Explorer, choose Run All to rerun the test. 赤色/緑色のステータス バーはテストが成功したことを示す緑色になり、テストは [成功したテスト] グループに移動します。The red/green bar turns green indicating the test passed, and the test is moved to the Passed Tests group.

単体テストを使用してコードを改良するUse unit tests to improve your code

このセクションでは、分析の反復処理、単体テストの進展、およびリファクタリングが、実稼働のコードの堅牢性と有効性を高めるうえでどのように役立つかを説明します。This section describes how an iterative process of analysis, unit test development, and refactoring can help you make your production code more robust and effective.

問題を分析するAnalyze the issues

有効な金額が Debit メソッドで正しく差し引かれているかどうかを確認するテスト メソッドを作成しました。You've created a test method to confirm that a valid amount is correctly deducted in the Debit method. 引き落とし金額が次のいずれかである場合、メソッドが ArgumentOutOfRangeException をスローすることを確認します。Now, verify that the method throws an ArgumentOutOfRangeException if the debit amount is either:

  • 残高よりも大きい、またはgreater than the balance, or
  • 0 未満。less than zero.

テスト メソッドを作成するCreate the test methods

引き落とし金額が 0 未満の場合の正しい動作を検証するテスト メソッドを作成します。Create a test method to verify correct behavior when the debit amount is less than zero:

[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange()
{
    // Arrange
    double beginningBalance = 11.99;
    double debitAmount = -100.00;
    BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);

    // Act
    account.Debit(debitAmount);

    // Assert is handled by the ExpectedException attribute on the test method.
}

ExpectedExceptionAttribute 属性を使用して、正しい例外がスローされたことをアサートします。Use the ExpectedExceptionAttribute attribute to assert that the correct exception has been thrown. この属性により、 ArgumentOutOfRangeException がスローされない限り、テストは失敗します。The attribute causes the test to fail unless an ArgumentOutOfRangeException is thrown. 引き落とし金額が 0 未満の場合、より汎用的な ApplicationException をスローするようにテスト対象のメソッドを一時的に変更すると、テストは正しく動作します。つまり失敗します。If you temporarily modify the method under test to throw a more generic ApplicationException when the debit amount is less than zero, the test behaves correctly—that is, it fails.

引き出し金額が残高を上回るケースをテストするには、次の手順を実行します。To test the case when the amount withdrawn is greater than the balance, do the following steps:

  1. Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRangeという新しいテスト メソッドを作成します。Create a new test method named Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange.

  2. メソッド本体を Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange から新しいメソッドにコピーします。Copy the method body from Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange to the new method.

  3. debitAmount を、残高を上回る数値に設定します。Set the debitAmount to a number greater than the balance.

テストを実行Run the tests

2 つのテスト メソッドを実行すると、テストが正しく機能することがわかります。Running the two test methods demonstrates that the tests work correctly.

分析を継続するContinue the analysis

一方で、最後の 2 つのテスト メソッドには問題もあります。However, the last two test methods are also troubling. いずれかのテストが実行されたときに、テスト対象のメソッドのどの条件が例外をスローするかを特定することはできません。You can't be certain which condition in the method under test throws the exception when either test is run. 負の引き落とし金額または残高よりも大きい金額の 2 つの条件を区別するいくつかの方法を利用すると、テストの信頼が高くなります。Some way of differentiating the two conditions, that is a negative debit amount or an amount greater than the balance, would increase your confidence in the tests.

テスト対象のメソッドをもう一度確認すると、引数の名前をパラメーターとして使用する ArgumentOutOfRangeException コンストラクターが両方の条件ステートメントで使用されていることがわかります。Look at the method under test again, and notice that both conditional statements use an ArgumentOutOfRangeException constructor that just takes name of the argument as a parameter:

throw new ArgumentOutOfRangeException("amount");

はるかに豊富な情報を報告するために使用できるコンストラクターがあります。ArgumentOutOfRangeException(String, Object, String) には、引数の名前、引数の値、ユーザー定義のメッセージが含まれています。There is a constructor you can use that reports far richer information: ArgumentOutOfRangeException(String, Object, String) includes the name of the argument, the argument value, and a user-defined message. このコンストラクターを使用するようにテスト対象のメソッドをリファクタリングできます。You can refactor the method under test to use this constructor. さらに、一般に公開されている型メンバーを使用して、エラーを指定できます。Even better, you can use publicly available type members to specify the errors.

テスト対象のコードをリファクタリングするRefactor the code under test

最初に、エラー メッセージの 2 つの定数をクラス スコープで定義します。First, define two constants for the error messages at class scope. これらをテスト対象のクラスに入れます (Bank)。Put these in the class under test (Bank):

public const string DebitAmountExceedsBalanceMessage = "Debit amount exceeds balance";
public const string DebitAmountLessThanZeroMessage = "Debit amount is less than zero";

次に、Debit メソッドの 2 つの条件ステートメントを変更します。Then, modify the two conditional statements in the Debit method:

    if (amount > m_balance)
    {
        throw new ArgumentOutOfRangeException("amount", amount, DebitAmountExceedsBalanceMessage);
    }

    if (amount < 0)
    {
        throw new ArgumentOutOfRangeException("amount", amount, DebitAmountLessThanZeroMessage);
    }

テスト メソッドをリファクタリングするRefactor the test methods

ExpectedException テスト メソッド属性を削除し、代わりにスローされた例外をキャッチし、関連付けられたメッセージを確認します。Remove the ExpectedException test method attribute and instead, catch the thrown exception and verify its associated message. StringAssert.Contains メソッドには、2 つの文字列を比較する機能があります。The StringAssert.Contains method provides the ability to compare two strings.

Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange は次のようになります。Now, the Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange might look like this:

[TestMethod]
public void Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange()
{
    // Arrange
    double beginningBalance = 11.99;
    double debitAmount = 20.0;
    BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);

    // Act
    try
    {
        account.Debit(debitAmount);
    }
    catch (ArgumentOutOfRangeException e)
    {
        // Assert
        StringAssert.Contains(e.Message, BankAccount.DebitAmountExceedsBalanceMessage);
    }
}

再テストする、書き換える、再分析するRetest, rewrite, and reanalyze

テスト対象のメソッドにバグがあっても Debit メソッドが ArgumentOutOfRangeExceptionスローせず、例外と共に正しいメッセージを出力するとします。Assume there's a bug in the method under test, and the Debit method doesn't even throw an ArgumentOutOfRangeException, nevermind output the correct message with the exception. 現在、テスト メソッドはこのケースを処理しません。Currently, the test method doesn't handle this case. debitAmount 値が有効な場合 (つまり、残高未満だが 0 よりは大きい場合)、例外はキャッチされないので、アサートはキャッチされません。If the debitAmount value is valid (that is, less than the balance but greater than zero), no exception is caught, so the assert never fires. それでも、テスト メソッドは成功します。Yet, the test method passes. これは適切ではありません。例外がスローされない場合はテスト メソッドが失敗することを想定しているためです。This is not good, because you want the test method to fail if no exception is thrown.

これはテスト メソッドのバグです。This is a bug in the test method. この問題を解決するには、テスト メソッドの最後に Fail アサートを追加して、例外がスローされないケースを処理するようにします。To resolve the issue, add an Fail assert at the end of the test method to handle the case where no exception is thrown.

ただし、テストを再実行すると、正しい例外がキャッチされた場合にテストが失敗したことが示されます。But rerunning the test shows that the test now fails if the correct exception is caught. catch ブロックは例外をキャッチしますが、メソッドは引き続き実行され、新しい Fail アサート時に失敗します。The catch block catches the exception, but the method continues to execute and it fails at the new Fail assert. この問題を解決するには、catch ブロックの StringAssert の後に return ステートメントを追加します。To resolve this problem, add a return statement after the StringAssert in the catch block. テストを再実行して、この問題が解決したことを確認します。Rerunning the test confirms that you've fixed this problem. Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange の最終バージョンは、次のようになります。The final version of the Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange looks like this:

[TestMethod]
public void Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange()
{
    // Arrange
    double beginningBalance = 11.99;
    double debitAmount = 20.0;
    BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);

    // Act
    try
    {
        account.Debit(debitAmount);
    }
    catch (ArgumentOutOfRangeException e)
    {
        // Assert
        StringAssert.Contains(e.Message, BankAccount.DebitAmountExceedsBalanceMessage);
        return;
    }

    Assert.Fail("The expected exception was not thrown.");
}

テスト コードを改善することで、より堅牢で有益なテスト方法になりました。The improvements to the test code led to more robust and informative test methods. ただし、もっと重要な点は、テスト対象のコードも改善されたことです。But more importantly, they also improved the code under test.