コードの増分変更によってコードが正しく動作し続けるのに役立つ単体テストを作成します。 単体テストの作成に使用できるフレームワークがいくつかあります。これには、サード パーティによって開発されたフレームワークも含まれます。 一部のテスト フレームワークは、さまざまな言語またはプラットフォームでのテストに特化されています。 テスト エクスプローラーには、これらのフレームワークの単体テスト用の 1 つのインターフェイスが用意されています。 テスト エクスプローラーの詳細については、「テスト エクスプローラーを使用した単体テストの実行」と「テスト エクスプローラーに関する FAQ」を参照してください。
このチュートリアルでは、Microsoft Test Framework (MSTest) を使用して C# でテスト済みメソッドを開発する方法について説明します。 他の言語や他のテスト フレームワーク (NUnit など) に簡単に適応させることができます。 詳細については、「サードパーティの単体テスト フレームワークインストールする」を参照してください。
テストを作成してコードを生成する
.NET または .NET Standard 用の C# クラス ライブラリ プロジェクトを作成します。 このプロジェクトには、テストするコードが含まれます。 プロジェクトに MyMath という名前を 付けます。
同じソリューションで、.NET 用の新しい MSTest テスト プロジェクトを追加します。
Visual Studio 2019 バージョン 16.9 では、MSTest プロジェクト テンプレート名は 単体テスト プロジェクトです。
テスト プロジェクトに MathTests という名前を付けます。
テスト プロジェクトで、特定の入力に対して取得された結果を検証する簡単なテスト メソッドを記述します。
Test1
またはUnitTest1
クラスに次のコードを追加します。[TestMethod] public void BasicRooterTest() { // Create an instance to test: Rooter rooter = new Rooter(); // Define a test input and output value: double expectedResult = 2.0; double input = expectedResult * expectedResult; // Run the method under test: double actualResult = rooter.SquareRoot(input); // Verify the result: Assert.AreEqual(expectedResult, actualResult, delta: expectedResult / 100); }
テスト コードから型を生成します。
Rooter
にカーソルを置き、電球メニューを開きます。[ 新しい型の生成] を選択します。
[ Generate type 'Rooter'>Generate new type] を選択します。
[ タイプの生成 ]ダイアログ ボックスで、[ プロジェクト ]を [MyMath]、[クラス ライブラリ プロジェクト]の順に設定し、[ OK]を選択します。
テスト コードからメソッドを生成します。
SquareRoot
にカーソルを置き、電球アイコンメニューから 'SquareRoot' メソッドを生成 または 'Rooter.SquareRoot' メソッドを生成 を選択します。単体テストを実行します。
テスト エクスプローラーを開きます。
[テスト] メニューからテスト エクスプローラーを開くには、[テスト エクスプローラー] を選択します。
[テスト] メニューからテスト エクスプローラーを開くには、Windows>Test Explorer を選択します。
テスト エクスプローラーで、[すべて実行] ボタンを選択してテストを実行します。
ソリューションがビルドされ、テストが実行され、失敗します。
テストの名前を選択します。
テストの詳細は、[テストの 詳細の概要 ] ウィンドウに表示されます。
[スタック トレース] の一番上のリンクを選択して、テストが失敗した場所にジャンプします。
この時点で、テストが成功するように変更できるテストとスタブを作成しました。
コードの変更を確認する
Class1.cs ファイルで、
SquareRoot
のコードを改善します。public double SquareRoot(double input) { return input / 2; }
テスト エクスプローラーで、[すべて実行] を選択します。
ソリューションがビルドされ、テストが実行されて合格します。
入力の範囲を拡張する
すべてのケースでコードが動作するという確信を高めるには、より広い範囲の入力値を試すテストを追加します。
ヒント
合格した既存のテストを変更しないでください。 代わりに、新しいテストを追加します。 ユーザー要件が変更された場合にのみ、既存のテストを変更します。 このポリシーは、コードの拡張に取り組む際に、既存の機能を失わないようにするのに役立ちます。
テスト クラスに、入力値の範囲を試行する次のテストを追加します。
[TestMethod] public void RooterValueRange() { // Create an instance to test. Rooter rooter = new Rooter(); // Try a range of values. for (double expected = 1e-8; expected < 1e+8; expected *= 3.2) { RooterOneValue(rooter, expected); } } private void RooterOneValue(Rooter rooter, double expectedResult) { double input = expectedResult * expectedResult; double actualResult = rooter.SquareRoot(input); Assert.AreEqual(expectedResult, actualResult, delta: expectedResult / 1000); }
テスト エクスプローラーで、[すべて実行] を選択します。
新しいテストは失敗します (ただし、最初のテストは合格します)。 障害点を見つけるには、失敗したテストを選択し、[ テストの詳細の概要 ] ウィンドウで詳細を確認します。
テスト対象のメソッドを調べて、何が間違っているかを確認します。
SquareRoot
コードを次のように変更します。public double SquareRoot(double input) { double result = input; double previousResult = -input; while (Math.Abs(previousResult - result) > result / 1000) { previousResult = result; result = result - (result * result - input) / (2 * result); } return result; }
テスト エクスプローラーで、[すべて実行] を選択します。
両方のテストに合格しました。
例外的なケースのテストを追加する
負の入力の新しいテストを追加します。
[TestMethod] public void RooterTestNegativeInput() { Rooter rooter = new Rooter(); Assert.ThrowsException<ArgumentOutOfRangeException>(() => rooter.SquareRoot(-1)); }
テスト エクスプローラーで、[すべて実行] を選択します。
新しいテストは失敗します。
テスト ループの対象のメソッドの場合は、テスト エクスプローラーのツール バーの [キャンセル] を選択します。 テストの実行が停止し、失敗します。
メソッドの先頭に次の
SquareRoot
ステートメントを追加して、if
コードを修正します。public double SquareRoot(double input) { if (input <= 0.0) { throw new ArgumentOutOfRangeException(); } ...
テスト エクスプローラーで、[すべて実行] を選択します。
すべてのテストに合格します。
テスト対象のコードをリファクタリングする
コードをリファクタリングしますが、テストは変更しないでください。
ヒント
リファクタリングとは、コードのパフォーマンスを向上させたり、理解しやすくしたりすることを目的とした変更です。 コードの動作を変更することを意図していないため、テストは変更されません。
リファクタリング手順は、機能を拡張する手順とは別に実行することをお勧めします。 テストを変更せずに保持すると、リファクタリング中に誤ってバグが発生しなかったという確信が得ることができます。
result
メソッドでSquareRoot
を計算する行を次のように変更します。public double SquareRoot(double input) { if (input <= 0.0) { throw new ArgumentOutOfRangeException(); } double result = input; double previousResult = -input; while (Math.Abs(previousResult - result) > result / 1000) { previousResult = result; result = (result + input / result) / 2; //was: result = result - (result * result - input) / (2*result); } return result; }
[ すべて実行] を選択し、すべてのテストが引き続き合格することを確認します。