UWP アプリの Visual C# コードの単体テストUnit testing Visual C# code in a UWP app

このトピックでは、UWP アプリの Visual C# クラスの単体テストを作成する方法の 1 つについて説明します。This topic describes one way to create unit tests for a Visual C# class in a UWP app. Rooter クラスは、指定した数値の平方根の概数を計算する関数を実装することによって、微積分の限界理論の不明瞭なメモリを示します。The Rooter class demonstrates vague memories of limit theory from calculus by implementing a function that calculates an estimate of the square root of a given number. Maths アプリケーションではこの関数を使用して、数学で実行できる楽しいことをユーザーに示すことができます。The Maths app can then use this function to show a user the fun things that can be done with math.

このトピックでは、開発の第一歩として単体テストを使用する方法を示します。This topic demonstrates how to use unit testing as the first step in development. この方法ではまず、テスト対象のシステムの特定の動作を検証するテスト メソッドを作成し、テストに合格するコードを記述します。In this approach, you first write a test method that verifies a specific behavior in the system that you are testing and then you write the code that passes the test. 後述する手順の順序を変更することにより、この方法を逆にして、テストするコードを最初に記述し、単体テストを作成することができます。By making changes in the order of the following procedures, you can reverse this strategy to first write the code that you want to test and then write the unit tests.

このトピックでは、テストする単体テストと DLL に 1 つの Visual Studio ソリューションと個別のプロジェクトも作成します。This topic also creates a single Visual Studio solution and separate projects for the unit tests and the DLL that you want to test. また、DLL プロジェクトに単体テストを直接含めることも、単体テストと DLL ごとに個別のソリューションを作成することもできます。You can also include the unit tests directly in the DLL project, or you can create separate solutions for the unit tests and the DLL.

ソリューションと単体テスト プロジェクトを作成するCreate the solution and the unit test project

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

  2. [新しいプロジェクト] ダイアログ ボックスで [インストール済み] > [Visual C#] の順に展開し、[Windows ユニバーサル] を選択します。In the New Project dialog box, expand Installed > Visual C# and choose Windows Universal. プロジェクト テンプレートの一覧の [新しいアプリケーション] をクリックします。Then choose Blank App from the list of project templates.

  3. プロジェクトに「Maths」という名前を付け、[ソリューションのディレクトリを作成] チェックボックスがオンになっていることを確認します。Name the project Maths and make sure Create directory for solution is selected.

  4. ソリューション エクスプローラーでソリューション名をクリックし、ショートカット メニューの [追加] をクリックし、[新しい項目] をクリックします。In Solution Explorer, choose the solution name, choose Add from the shortcut menu, and then choose New Project.

  5. [新しいプロジェクト] ダイアログ ボックスで [インストール済み][Visual C#] の順に展開し、[Windows ユニバーサル] を選択します。In the New Project dialog box, expand Installed, then expand Visual C# and choose Windows Universal . プロジェクト テンプレートの一覧の [単体テスト アプリ (ユニバーサル Windows)] を選択します。Then choose Unit Test App (Universal Windows) from the list of project templates.

  6. Visual Studio エディターで UnitTest1.cs を開きます。Open UnitTest1.cs in the Visual Studio editor.

    
    using System;  
    using System.Collections.Generic;  
    using System.Linq;  
    using System.Text;  
    using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;  
    using Maths;  
    
    namespace RooterTests  
    {  
        [TestClass]  
        public class UnitTest1  
    
            [TestMethod]  
            public void TestMethod1()  
            {  
    
            }  
    

    次の点に注意してください。Note that:

    1. 各テストは [TestMethod] を使用して定義されます。Each test is defined by using the [TestMethod]. テスト メソッドは void を返す必要があり、パラメーターをとることはできません。A test method must return void and can't have any parameters.

    2. テスト メソッドは [TestClass] の属性で装飾されたクラスに配置する必要があります。Test methods must be in a class decorated with the [TestClass] attribute.

      テストの実行時に、各テスト クラスのインスタンスが作成されます。When the tests are run, an instance of each test class is created. テスト メソッドが呼び出される順序は決まっていません。The test methods are called in an unspecified order.

    3. 各モジュール、クラス、またはメソッドの前後に呼び出される特殊なメソッドを定義することができます。You can define special methods that are invoked before and after each module, class, or method. 詳細については、MSDN ライブラリの「単体テストでの Microsoft.VisualStudio.TestTools.UnitTesting のメンバーの使用」を参照してください。For more information, see Using Microsoft.VisualStudio.TestTools.UnitTesting Members in Unit Tests in the MSDN Library.

テストがテスト エクスプローラーで実行されることを確認するVerify that the tests run in Test Explorer

  1. UnitTest1.cs ファイルの TestMethod1 のテスト コードを挿入します。Insert some test code in TestMethod1 of the UnitTest1.cs file:

    
    [TestMethod]  
    public void TestMethod1()  
    {  
        Assert.AreEqual(0, 0);  
    }  
    

    Assert クラスは、テスト メソッドで結果を確認するために使用するいくつかの静的メソッドを提供することに注意してください。Notice that the Assert class provides several static methods that you can use to verify results in test methods.

  2. [テスト] メニューの [実行] をポイントし、[すべて実行] をクリックします。On the Test menu, choose Run and then choose Run All.

    テスト プロジェクトがビルドされ、実行されます。The test project builds and runs. テスト エクスプローラーのウィンドウが表示され、テストが [成功したテスト] に表示されます。The Test Explorer window appears, and the test is listed under Passed Tests. ウィンドウの下部の [概要] ウィンドウに、選択したテストに関する詳細情報が表示されます。The Summary pane at the bottom of the window provides additional details about the selected test.

    テスト エクスプ ローラーTest Explorer

Maths プロジェクトに Rooter クラスを追加します。Add the Rooter class to the Maths project

  1. ソリューション エクスプローラーでプロジェクト名の [Maths] を選択します。In Solution Explorer, choose the Maths project name. ショートカット メニューの [追加] をポイントし、[クラス] をクリックします。From the shortcut menu, choose Add, and then Class.

  2. クラス ファイルに Rooter.cs という名前を付けます。Name the class file Rooter.cs

  3. Rooter クラスの Rooter.cs ファイルに次のコードを追加します。Add the following code to the Rooter class Rooter.cs file:

    
    public Rooter()  
    {  
    }  
    
    // estimate the square root of a number  
    public double SquareRoot(double x)  
    {  
        return 0.0;  
    }  
    

    Rooter クラスは、コンストラクターと SqareRoot エスティメーターのメソッドを宣言します。The Rooter class declares a constructor and the SqareRoot estimator method.

  4. SqareRoot メソッドは、テスト設定の基本的な構造をテストするための必要最小限の実装にすぎません。The SqareRoot method is only a minimal implementation, just enough to test the basic structure of the testing setup.

アプリケーション プロジェクトにテスト プロジェクトを結合するCouple the test project to the app project

  1. RooterTests プロジェクトに Maths アプリケーションへの参照を追加します。Add a reference to the Maths app to the RooterTests project.

    1. ソリューション エクスプローラーで、RooterTests プロジェクトを選択し、ショートカット メニューの [参照の追加...] をクリックします。In Solution Explorer, choose the RooterTests project and then choose Add Reference... on the shortcut menu.

    2. [参照の追加 - RooterTests] ダイアログ ボックスの [ソリューション] を展開し、[プロジェクト] をクリックします。On the Add Reference - RooterTests dialog box, expand Solution and choose Projects. 次に [Maths] 項目を選択します。Then select the Maths item.

      Maths プロジェクトへの参照の追加Add a reference to the Maths project

  2. 使用するステートメントを UnitTest1.cs ファイルに追加します。Add a using statement to the UnitTest1.cs file:

    1. UnitTest1.cs を開きます。Open UnitTest1.cs.

    2. using Microsoft.VisualStudio.TestPlatform.UnitTestFramework; 行の下に次のコードを追加します。Add this code below the using Microsoft.VisualStudio.TestPlatform.UnitTestFramework; line:

      using Maths;  
      
  3. Rooter 関数を使用するテストを追加します。Add a test that uses the Rooter function. UnitTest1.cpp に次のコードを追加します。Add the following code to UnitTest1.cpp:

    [TestMethod]  
    public void BasicTest()  
    {  
        Maths.Rooter rooter = new Rooter();  
        double expected = 0.0;  
        double actual = rooter.SquareRoot(expected * expected);  
        double tolerance = .001;  
        Assert.AreEqual(expected, actual, tolerance);  
    }  
    
  4. ソリューションをビルドします。Build the solution.

    新しいテストがテスト エクスプローラーの [テストを実行しない] ノードに表示されます。The new test appears in Test Explorer in the Not Run Tests node.

  5. テスト エクスプローラーで [すべて実行]をクリックします。In Test Explorer, choose Run All.

    基本テスト成功Basic Test passed

    テストとコード プロジェクトをセット アップして、コード プロジェクトで関数を実行するテストを実行できることを確認しました。You have set up the test and the code projects, and verified that you can run tests that run functions in the code project. ここで、実際のテストおよびコードの記述を開始できます。Now you can begin to write real tests and code.

テストを繰り返し増やして成功させるIteratively augment the tests and make them pass

  1. 新しいテストを追加します。Add a new test:

    [TestMethod]  
    public void RangeTest()  
    {  
        Rooter rooter = new Rooter();  
        for (double v = 1e-6; v < 1e6; v = v * 3.2)  
        {  
            double expected = v;  
            double actual = rooter.SquareRoot(v*v);  
            double tolerance = ToleranceHelper(expected);  
            Assert.AreEqual(expected, actual, tolerance);  
        }  
    }  
    

    ヒント

    合格したテスト内容を変更しないことをお勧めします。We recommend that you do not change tests that have passed. 代わりに、新しいテストを追加し、テストが合格するようにコードを更新してから別のテストを追加する、という過程を繰り返します。Instead, add a new test, update the code so that the test passes, and then add another test, and so on.

    ユーザーが要件を変更したら、正しくなくなったテストを無効にします。When your users change their requirements, disable the tests that are no longer correct. 新しいテストを作成し、一度に 1 つずつ、同じ増分方式で処理するようにします。Write new tests and make them work one at a time, in the same incremental manner.

  2. テスト エクスプローラーで [すべて実行]をクリックします。In Test Explorer, choose Run All.

  3. テストが失敗します。The test fails.

    RangeTest 失敗The RangeTest fails

    ヒント

    そのテストを作成した直後に、各テストが失敗することを検証します。Immediately after you have written it, verify that each test fails. これは、絶対に失敗しないテストを記述するという簡単なミスを避けることに役立ちます。This helps you avoid the easy mistake of writing a test that never fails.

  4. 新しいテストが成功するように、テスト対象のコードを増やします。Enhance the code under test so that the new test passes. Rooter.csSqareRoot 関数を次のように変更します。Change the SqareRoot function in Rooter.cs to this:

    public double SquareRoot(double x)  
    {  
        double estimate = x;  
        double diff = x;  
        while (diff > estimate / 1000)  
        {  
            double previousEstimate = estimate;  
            estimate = estimate - (estimate * estimate - x) / (2 * estimate);  
            diff = Math.Abs(previousEstimate - estimate);  
        }  
        return estimate;  
    }  
    
  5. ソリューションをビルドし、テスト エクスプ ローラーで [すべて実行]を選択します。Build the solution and then in Test Explorer, choose Run All.

    3 つのテストはすべて成功しました。All three tests now pass.

ヒント

一度に 1 つのテストを追加してコードを開発します。Develop code by adding tests one at a time. 各反復処理の後にすべてのテストが合格することを確認します。Make sure that all the tests pass after each iteration.

失敗したテストをデバッグするDebug a failing test

  1. UnitTest1.cs に別のテストを追加します。Add another test to UnitTest1.cs:

    // Verify that negative inputs throw an exception.  
    [TestMethod]  
    public void NegativeRangeTest()  
    {  
        string message;  
        Rooter rooter = new Rooter();  
        for (double v = -0.1; v > -3.0; v = v - 0.5)  
        {  
            try  
            {  
                // Should raise an exception:  
                double actual = rooter.SquareRoot(v);  
    
                message = String.Format("No exception for input {0}", v);  
                Assert.Fail(message);  
            }  
            catch (ArgumentOutOfRangeException ex)  
            {  
                continue; // Correct exception.  
            }  
            catch (Exception e)  
            {  
                message = String.Format("Incorrect exception for {0}", v);  
                Assert.Fail(message);  
            }  
        }  
    }  
    
  2. テスト エクスプローラーで [すべて実行]をクリックします。In Test Explorer, choose Run All.

    テストが失敗します。The test fails. テスト エクスプローラーでテスト名を選択します。Choose the test name in Test Explorer. 失敗したアサーションが強調表示されます。The failed assertion is highlighted. エラー メッセージは、テスト エクスプ ローラーの [詳細] ウィンドウに表示されます。The failure message is visible in the detail pane of Test Explorer.

    Negativerangetest 失敗NegativeRangeTests failed

  3. テストが失敗した理由を表示するには、関数をステップ実行します。To see why the test fails, step through the function:

    1. SquareRoot 関数の先頭にブレークポイントを設定します。Set a breakpoint at the start of the SquareRoot function.

    2. 失敗したテストのショートカット メニューで [選択したテストのデバッグ]をクリックします。On the shortcut menu of the failed test, choose Debug Selected Tests.

      実行がブレークポイントで停止したら、コードをステップ実行します。When the run stops at the breakpoint, step through the code.

    3. 例外をキャッチするには、Rooter メソッドにコードを追加します。Add code to the Rooter method to catch the exception:

      public double SquareRoot(double x)  
      {  
          if (x < 0.0)  
          {  
              throw new ArgumentOutOfRangeException();  
      }  
      
    4. テスト エクスプローラーで [すべて実行] をクリックして、修正されたメソッドをテストし、回帰が生じていないことを確認します。In Test Explorer, choose Run All to test the corrected method and make sure that you haven't introduced a regression.

    今回は、すべてのテストに合格します。All tests now pass.

    すべてのテストの成功All tests pass

コードをリファクタリングするRefactor the code

SquareRoot 関数の中心的な計算を簡素化します。Simplify the central calculation in the SquareRoot function.

  1. 結果の実装を変更するChange the result implementation

    // old code  
    //result = result - (result*result - v)/(2*result);  
    // new code  
    result = (result + v/result) / 2.0;  
    
  2. [すべて実行] をクリックして、リファクタリングされたメソッドをテストし、回帰が生じていないことを確認します。Choose Run All to test the refactored method and make sure that you haven't introduced a regression.

ヒント

安定した一連の適切な単体テストを実行することで、コードを変更したときにバグが生じていないことを確信できます。A stable set of good unit tests gives confidence that you have not introduced bugs when you change the code.

テスト コードをリファクタリングして、重複したコードを削除します。Refactor the test code to eliminate duplicated code.

RangeTest メソッドでは、Assert メソッドで使用される許容変数の分母をハードコーディングします。Note that the RangeTest method hard codes the denominator of the tolerance variable that is used in the Assert method. 同じ許容値計算を使用するテストを追加する場合、ハードコーディングされた値を複数の場所で使用すると、エラーの原因となる可能性があります。If you plan to add additional tests that use the same tolerance calculation, the use of a hard-coded value in multiple locations could lead to errors.

  1. 代わりに、Unit1Test クラスにプライベート メソッドを追加して、許容値を計算し、そのメソッドを呼び出します。Add a private method to the Unit1Test class to calculate the tolerance value and then call that method instead.

    private double ToleranceHelper(double expected)  
    {  
        return expected / 1000;  
    }  
    
    ...  
    
    [TestMethod]  
    public void RangeTest()  
    {  
        ...  
        // old code  
        // double tolerance = expected/1000;  
        // new code  
        double tolerance = ToleranceHelper(expected);  
        Assert.AreEqual(expected, actual, tolerance);  
    }  
    ...  
    
  2. [すべて実行] をクリックして、リファクタリングされたメソッドをテストし、エラーが生じていないことを確認します。Choose Run All to test the refactored method and make sure that you haven't introduced an error.

注意

テスト クラスにヘルパー メソッドを追加する場合に、メソッドに [TestMethod] 属性を追加しないでください。To add a helper method to a test class, do not add the [TestMethod] attribute to the method. テスト エクスプローラーでは、実行するメソッドが登録されません。Test Explorer does not register the method to be run.