Live Unit Testing の概要
Visual Studio ソリューションで Live Unit Testing を有効にすると、テスト カバレッジとテストの状態が視覚的に示されます。 また、Live Unit Testing では、コードが変更されるたびにテストが動的に実行され、変更が原因でテストが不合格になった場合にはただちに通知されます。
Live Unit Testing を使用すると、.NET Framework、.NET Core、または .NET 5以降のいずれかを対象とするソリューションをテストすることができます。 このチュートリアルでは、.NET を対象とする簡単なクラス ライブラリを作成することによって Live Unit Testing の使用方法を学習し、それをテストするために .NET を対象とする MSTest プロジェクトを作成します。
完全な C# ソリューションは、GitHub 上の MicrosoftDocs/visualstudio-docs リポジトリからダウンロードできます。
前提条件
このチュートリアルでは、Visual Studio Enterprise エディションと .NET デスクトップの開発ワークロードがインストールされている必要があります。
ソリューションとクラス ライブラリ プロジェクトを作成する
最初に、単一の .NET クラス ライブラリ プロジェクト StringLibrary で構成される UtilityLibraries という名前の Visual Studio ソリューションを作成します。
このソリューションは、1 つまたは複数のプロジェクト用のコンテナーにすぎません。 空のソリューションを作成するには、Visual Studio を開き、次の手順を行います。
Visual Studio の最上位のメニューから、[ファイル]>[新規作成]>[プロジェクト] の順に選択します。
テンプレートの検索ボックスに「ソリューション」と入力して、[空のソリューション] テンプレートを選択します。 プロジェクトに UtilityLibraries という名前を指定します。
ソリューションの作成を終了します。
ソリューションを作成したので、次に、文字列を操作するための複数の拡張メソッドが含まれる StringLibrary という名前のクラス ライブラリを作成します。
ソリューション エクスプローラーで、UtilityLibraries ソリューションを右クリックし、[追加]>[新しいプロジェクト] の順に選択します。
テンプレートの検索ボックスに「クラス ライブラリ」と入力して、.NET または .NET Standard を対象とする [クラス ライブラリ] テンプレートを選択してください。 次へ をクリックします。
プロジェクトに StringLibrary という名前を付けます。
作成 を選択してプロジェクトを作成します。
コード エディター内の既存のコードをすべて、次のコードに置き換えます。
using System; namespace UtilityLibraries { public static class StringLibrary { public static bool StartsWithUpper(this string s) { if (String.IsNullOrWhiteSpace(s)) return false; return Char.IsUpper(s[0]); } public static bool StartsWithLower(this string s) { if (String.IsNullOrWhiteSpace(s)) return false; return Char.IsLower(s[0]); } public static bool HasEmbeddedSpaces(this string s) { foreach (var ch in s.Trim()) { if (ch == ' ') return true; } return false; } } }
StringLibrary には 3 つの静的メソッドがあります。
StartsWithUpper
は、文字列が大文字で始まる場合はtrue
を返し、それ以外の場合にはfalse
を返します。StartsWithLower
は、文字列が小文字で始まる場合はtrue
を返し、それ以外の場合にはfalse
を返します。HasEmbeddedSpaces
は、埋め込み空白文字が文字列に含まれている場合はtrue
を返し、それ以外の場合にはfalse
を返します。
Visual Studio の最上位メニューから、[ビルド]>[ソリューションのビルド] の順に選択します。 ビルドは成功するはずです。
テスト プロジェクトの作成
次のステップとして、StringLibrary ライブラリをテストする単体テスト プロジェクトを作成します。 次の手順を実行して、単体テストを作成します。
ソリューション エクスプローラーで、UtilityLibraries ソリューションを右クリックし、[追加]>[新しいプロジェクト] の順に選択します。
テンプレート検索ボックスに「単体テスト」と入力し、言語として C# を選択してから、.NET テンプレートの [MSTest 単体テスト プロジェクト] を選択してください。 [次へ] をクリックします。
Note
Visual Studio 2019 バージョン 16.9 では、MSTest プロジェクト テンプレートの名前は単体テスト プロジェクトです。
プロジェクトに「StringLibraryTests」という名前を指定し、[次へ] をクリックします。
推奨されるターゲット フレームワークまたは .NET 8 を選択し、[作成] を選択します。
Note
このはじめに (チュートリアル) では、Live Unit Testing を MSTest テスト フレームワークと一緒に使用します。 また、xUnit および NUnit のテスト フレームワークを使用することもできます。
単体テスト プロジェクトは、テスト対象のクラス ライブラリに自動的にアクセスすることはできません。 テスト ライブラリへのアクセス権を付与するには、クラス ライブラリ プロジェクトへの参照を追加します。 これを行うには、
StringLibraryTests
プロジェクトを右クリックし、[追加]>[プロジェクト参照] の順に選択してください。 次の図に示すように、[参照マネージャー] ダイアログで、[ソリューション] タブが選択されていることを確認し、StringLibrary プロジェクトを選択します。テンプレートで入力されている定型的な単体テスト コードを次のコードに置き換えます。
using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using UtilityLibraries; namespace StringLibraryTest { [TestClass] public class UnitTest1 { [TestMethod] public void TestStartsWithUpper() { // Tests that we expect to return true. string[] words = { "Alphabet", "Zebra", "ABC", "Αθήνα", "Москва" }; foreach (var word in words) { bool result = word.StartsWithUpper(); Assert.IsTrue(result, $"Expected for '{word}': true; Actual: {result}"); } } [TestMethod] public void TestDoesNotStartWithUpper() { // Tests that we expect to return false. string[] words = { "alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство", "1234", ".", ";", " " }; foreach (var word in words) { bool result = word.StartsWithUpper(); Assert.IsFalse(result, $"Expected for '{word}': false; Actual: {result}"); } } [TestMethod] public void DirectCallWithNullOrEmpty() { // Tests that we expect to return false. string[] words = { String.Empty, null }; foreach (var word in words) { bool result = StringLibrary.StartsWithUpper(word); Assert.IsFalse(result, $"Expected for '{(word == null ? "<null>" : word)}': " + $"false; Actual: {result}"); } } } }
ツール バーの [保存] アイコンを選択してプロジェクトを保存します。
単体テスト コードには ASCII 以外の文字がいくつか含まれているため、既定の ASCII 形式でファイルを保存すると失われる文字が存在することを警告する次のようなダイアログが表示されます。
[その他のエンコードで保存] ボタンを選択します。
次の図に示すように、 [保存オプションの詳細設定] ダイアログの [エンコード] ドロップダウン リストから [Unicode (UTF-8 シグネチャ付き) - コードページ 65001] を選択します。
Visual Studio の最上位メニューから、 [ビルド]>[ソリューションのリビルド] の順に選択して、単体テスト プロジェクトをコンパイルします。
クラス ライブラリと、これに対する複数の単体テストが作成されました。 これで、Live Unit Testing を使用するために必要な準備作業が完了しました。
Live Unit Testing の有効化
StringLibrary クラス ライブラリ用のテストを作成しましたが、まだ実行していません。 Live Unit Testing を有効にすると、テストが自動的に実行されます。 Live Unit Testing を有効にするには、次の手順を実行します。
必要に応じて、StringLibrary のコードが含まれているコード エディター ウィンドウを選択します。 C# プロジェクトの Class1.cs か、Visual Basic プロジェクトの Class1.vb のどちらかです。 (このステップでは、Live Unit Testing を有効にした後で、テストの結果とコード カバレッジの範囲を視覚的に検査します。)
Visual Studio の最上位メニューから [テスト]>[Live Unit Testing]>[開始] の順に選びます。
リポジトリ ルートにユーティリティ プロジェクトとテスト プロジェクトの両方のソース ファイルへのパスが含まれるようにすることで、Live Unit Testing の構成を確認します。 [次へ] を選択し、[完了] を選択します。
[Live Unit Testing] ウィンドウで、[すべてのテストを含める] リンクを選択してください (または、[プレイリスト] ボタン アイコンを選択し、その下にあるすべてのテストを選択する [StringLibraryTest] を選択してください。次に、[プレイリスト] ボタンの選択を解除して編集モードを終了してください)。
Visual Studio によってプロジェクトが再構築され、Live Unit Test が開始され、すべてのテストが自動的に実行されます。
- Visual Studio によってプロジェクトが再構築され、Live Unit Test が開始され、すべてのテストが自動的に実行されます。
テストの実行が終了すると、[Live Unit Testing] に全体の結果と個々のテストの結果の両方が表示されます。 さらに、コード エディター ウィンドウには、テスト コードのカバレッジとテストの結果とが両方ともグラフィカルに表示されます。 次の図に示すように、3 つのテストはいずれも正常に実行されています。 また、テストは StartsWithUpper
メソッド内のすべてのコード パスをカバーしており、それらのテストはすべて正常に実行されていることがわかります (緑色のチェック マーク "✓" によって示されている)。 最後に、StringLibrary 内の他のメソッドにはコード カバレッジが設定されていないことがわかります (青い線 "➖" によって示されている)。
また、コード エディター ウィンドウ内の特定のコード カバレッジ アイコンを選択することにより、テスト カバレッジとテスト結果に関する詳細情報を取得することもできます。 この詳細を調べるには、次の手順を実行します。
StartsWithUpper
メソッド内のif (String.IsNullOrWhiteSpace(s))
という行に表示された緑のチェック マークをクリックします。 次の図に示すように、Live Unit Testing では、そのコード行が 3 つのテストでカバーされており、いずれのテストも正常に実行されたことが示されています。StartsWithUpper
メソッド内のreturn Char.IsUpper(s[0])
という行に表示された緑のチェック マークをクリックします。 次の図に示すように、Live Unit Testing では、そのコード行が 2 つのテストのみでカバーされており、いずれのテストも正常に実行されたことが示されています。
Live Unit Testing は、重大な問題として不完全なコード カバレッジを明らかにしています。 この問題は、次のセクションで取り上げます。
テスト カバレッジの展開
このセクションでは、単体テストを StartsWithLower
メソッドに展開します。 展開を行っている間も、Live Unit Testing によって引き続きコードのテストが実行されます。
コード カバレッジを StartsWithLower
メソッドに展開するには、次の手順を実行します。
次の
TestStartsWithLower
メソッドとTestDoesNotStartWithLower
メソッドをプロジェクトのテスト ソース コード ファイルに追加します。// Code to add to UnitTest1.cs [TestMethod] public void TestStartsWithLower() { // Tests that we expect to return true. string[] words = { "alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство" }; foreach (var word in words) { bool result = word.StartsWithLower(); Assert.IsTrue(result, $"Expected for '{word}': true; Actual: {result}"); } } [TestMethod] public void TestDoesNotStartWithLower() { // Tests that we expect to return false. string[] words = { "Alphabet", "Zebra", "ABC", "Αθήνα", "Москва", "1234", ".", ";", " "}; foreach (var word in words) { bool result = word.StartsWithLower(); Assert.IsFalse(result, $"Expected for '{word}': false; Actual: {result}"); } }
Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse
メソッドへの呼び出しのすぐ後に、次のコードを追加して、DirectCallWithNullOrEmpty
メソッドを変更します。// Code to add to UnitTest1.cs result = StringLibrary.StartsWithLower(word); Assert.IsFalse(result, $"Expected for '{(word == null ? "<null>" : word)}': " + $"false; Actual: {result}");
ソース コードを変更すると、Live Unit Testing によって新しいテストと変更したテストが自動的に実行されます。 次の図に示すように、追加した 2 つのテストと変更した 1 つのテストを含むすべてのテストが成功しています。
StringLibrary クラスのソース コードが含まれるウィンドウに切り替えます。 Live Unit Testing は、コード カバレッジが
StartsWithLower
メソッドに展開されていることを示します。
場合によっては、成功したテストがテスト エクスプローラーで、灰色で表示されることがあります。これは、テストが現在実行中であることを示すか、または最後に実行されてから、テストに影響する変更がコードに加えられていないために、テストが再度実行されなかったことを示します。
これまで、すべてのテストが成功しています。 次のセクションでは、テスト エラーを処理する方法について説明します。
テスト エラーの処理
このセクションでは、Live Unit Testing を使用してテスト エラーを識別し、トラブルシューティングを行い、対処する方法を説明します。 そのために、テスト カバレッジを HasEmbeddedSpaces
メソッドに展開します。
テスト ファイルに次のメソッドを追加します。
[TestMethod] public void TestHasEmbeddedSpaces() { // Tests that we expect to return true. string[] phrases = { "one car", "Name\u0009Description", "Line1\nLine2", "Line3\u000ALine4", "Line5\u000BLine6", "Line7\u000CLine8", "Line0009\u000DLine10", "word1\u00A0word2" }; foreach (var phrase in phrases) { bool result = phrase.HasEmbeddedSpaces(); Assert.IsTrue(result, $"Expected for '{phrase}': true; Actual: {result}"); } }
次の図に示すように、テストが実行されると、Live Unit Testing では
TestHasEmbeddedSpaces
メソッドが失敗したことが示されます。ライブラリ コードが表示されたウィンドウを選択します。 Live Unit Testing によって
HasEmbeddedSpaces
メソッドまでコード カバレッジが広げられています。 また、Live Unit Testing は、失敗したテストでカバーされている行に赤い "🞩" を追加することで、テスト エラーを報告しています。HasEmbeddedSpaces
メソッド シグネチャが含まれている行にマウス ポインターを合わせます。 次の図に示すように、Live Unit Testing では、メソッドが 1 つのテストでカバーされていることを知らせるヒントが表示されます。失敗した TestHasEmbeddedSpaces テストを選択します。 次の図に示すように、Live Unit Testing には、すべてのテストの実行やすべてのテストのデバッグなど、いくつかのオプションが用意されています。
[すべてデバッグ] を選択して、失敗したテストをデバッグします。
Visual Studio はデバッグ モードでテストを実行します。
テストでは、配列内の各文字列が
phrase
という名前の変数に割り当てられ、その変数がHasEmbeddedSpaces
メソッドに渡されます。 アサーション式が初めてfalse
になると、プログラムの実行は一時停止し、デバッガーが呼び出されます。Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue
のメソッド呼び出しに予期しない値が含まれていると、以下の図に示すような例外ダイアログが表示されます。さらに、次の図に示すように、Visual Studio で用意されているすべてのデバッグ ツールを使用することで、失敗したテストのトラブルシューティングを効果的に行うことができます。
[自動変数] ウィンドウで、
phrase
変数の値が "Name\tDescription" (配列の 2 番目の要素) になっていることに注目してください。 テスト メソッドは、この文字列がHasEmbeddedSpaces
に渡された場合にtrue
が返されるのを期待していますが、実際に返されるのはfalse
です。 タブ文字である "\t" が埋め込みスペースとして認識されていないのは明らかです。テスト プログラムの実行を続行するには、[デバッグ]>[続行] の順に選択するか、F5 キーを押すか、あるいはツールバーの [続行] ボタンをクリックします。 ハンドルされない例外が発生したため、テストは終了します。 これは、バグの予備調査を行う上で十分な情報です。
TestHasEmbeddedSpaces
(テスト ルーチン) で不適切な想定が行われたか、またはHasEmbeddedSpaces
がすべての埋め込みスペースを正しく認識していません。問題を診断し解決するには、
StringLibrary.HasEmbeddedSpaces
メソッドから始めます。HasEmbeddedSpaces
メソッドでの比較を確認します。 このメソッドでは、埋め込みスペースを U+0020 と見なしています。 ただし、Unicode Standard には、その他にも多くの空白文字が含まれています。 このことは、空白文字かどうかのテストが、ライブラリ コードで正しく行われていないことを示しています。等価比較を、System.Char.IsWhiteSpace メソッドへの呼び出しに置き換えます。
if (Char.IsWhiteSpace(ch))
失敗したテスト メソッドは、Live Unit Testing によって自動的に再実行されます。
更新された結果は、Live Unit Testing とコード エディター ウィンドウに表示されます。