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 を開き、次の手順を行います。

  1. Visual Studio の最上位のメニューから、[ファイル]>[新規作成]>[プロジェクト] の順に選択します。

  2. テンプレートの検索ボックスに「ソリューション」と入力して、[空のソリューション] テンプレートを選択します。 プロジェクトに UtilityLibraries という名前を指定します。

  3. ソリューションの作成を終了します。

ソリューションを作成したので、次に、文字列を操作するための複数の拡張メソッドが含まれる StringLibrary という名前のクラス ライブラリを作成します。

  1. ソリューション エクスプローラーで、UtilityLibraries ソリューションを右クリックし、[追加]>[新しいプロジェクト] の順に選択します。

  2. テンプレートの検索ボックスに「クラス ライブラリ」と入力して、.NET または .NET Standard を対象とする [クラス ライブラリ] テンプレートを選択してください。 次へ をクリックします。

  3. プロジェクトに StringLibrary という名前を付けます。

  4. 作成 を選択してプロジェクトを作成します。

  5. コード エディター内の既存のコードをすべて、次のコードに置き換えます。

    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 を返します。

  6. Visual Studio の最上位メニューから、[ビルド]>[ソリューションのビルド] の順に選択します。 ビルドは成功するはずです。

テスト プロジェクトの作成

次のステップとして、StringLibrary ライブラリをテストする単体テスト プロジェクトを作成します。 次の手順を実行して、単体テストを作成します。

  1. ソリューション エクスプローラーで、UtilityLibraries ソリューションを右クリックし、[追加]>[新しいプロジェクト] の順に選択します。

  2. テンプレート検索ボックスに「単体テスト」と入力し、言語として C# を選択してから、.NET テンプレートの [MSTest 単体テスト プロジェクト] を選択してください。 [次へ] をクリックします。

    Note

    Visual Studio 2019 バージョン 16.9 では、MSTest プロジェクト テンプレートの名前は単体テスト プロジェクトです。

  3. プロジェクトに「StringLibraryTests」という名前を指定し、[次へ] をクリックします。

  4. 推奨されるターゲット フレームワークまたは .NET 8 を選択し、[作成] を選択します。

    Note

    このはじめに (チュートリアル) では、Live Unit Testing を MSTest テスト フレームワークと一緒に使用します。 また、xUnit および NUnit のテスト フレームワークを使用することもできます。

  5. 単体テスト プロジェクトは、テスト対象のクラス ライブラリに自動的にアクセスすることはできません。 テスト ライブラリへのアクセス権を付与するには、クラス ライブラリ プロジェクトへの参照を追加します。 これを行うには、 StringLibraryTests プロジェクトを右クリックし、[追加]>[プロジェクト参照] の順に選択してください。 次の図に示すように、[参照マネージャー] ダイアログで、[ソリューション] タブが選択されていることを確認し、StringLibrary プロジェクトを選択します。

    [参照マネージャー] ダイアログ

    [参照マネージャー] ダイアログ

  6. テンプレートで入力されている定型的な単体テスト コードを次のコードに置き換えます。

    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}");
                }
            }
        }
    }
    
  7. ツール バーの [保存] アイコンを選択してプロジェクトを保存します。

    単体テスト コードには ASCII 以外の文字がいくつか含まれているため、既定の ASCII 形式でファイルを保存すると失われる文字が存在することを警告する次のようなダイアログが表示されます。

  8. [その他のエンコードで保存] ボタンを選択します。

    ファイルのエンコードを選択

    ファイルのエンコードを選択

  9. 次の図に示すように、 [保存オプションの詳細設定] ダイアログの [エンコード] ドロップダウン リストから [Unicode (UTF-8 シグネチャ付き) - コードページ 65001] を選択します。

    UTF-8 エンコードを選択

  10. Visual Studio の最上位メニューから、 [ビルド]>[ソリューションのリビルド] の順に選択して、単体テスト プロジェクトをコンパイルします。

クラス ライブラリと、これに対する複数の単体テストが作成されました。 これで、Live Unit Testing を使用するために必要な準備作業が完了しました。

Live Unit Testing の有効化

StringLibrary クラス ライブラリ用のテストを作成しましたが、まだ実行していません。 Live Unit Testing を有効にすると、テストが自動的に実行されます。 Live Unit Testing を有効にするには、次の手順を実行します。

  1. 必要に応じて、StringLibrary のコードが含まれているコード エディター ウィンドウを選択します。 C# プロジェクトの Class1.cs か、Visual Basic プロジェクトの Class1.vb のどちらかです。 (このステップでは、Live Unit Testing を有効にした後で、テストの結果とコード カバレッジの範囲を視覚的に検査します。)

  2. Visual Studio の最上位メニューから [テスト]>[Live Unit Testing]>[開始] の順に選びます。

  3. リポジトリ ルートにユーティリティ プロジェクトとテスト プロジェクトの両方のソース ファイルへのパスが含まれるようにすることで、Live Unit Testing の構成を確認します。 [次へ] を選択し、[完了] を選択します。

  1. [Live Unit Testing] ウィンドウで、[すべてのテストを含める] リンクを選択してください (または、[プレイリスト] ボタン アイコンを選択し、その下にあるすべてのテストを選択する [StringLibraryTest] を選択してください。次に、[プレイリスト] ボタンの選択を解除して編集モードを終了してください)。

  2. Visual Studio によってプロジェクトが再構築され、Live Unit Test が開始され、すべてのテストが自動的に実行されます。

  1. Visual Studio によってプロジェクトが再構築され、Live Unit Test が開始され、すべてのテストが自動的に実行されます。

テストの実行が終了すると、[Live Unit Testing] に全体の結果と個々のテストの結果の両方が表示されます。 さらに、コード エディター ウィンドウには、テスト コードのカバレッジとテストの結果とが両方ともグラフィカルに表示されます。 次の図に示すように、3 つのテストはいずれも正常に実行されています。 また、テストは StartsWithUpper メソッド内のすべてのコード パスをカバーしており、それらのテストはすべて正常に実行されていることがわかります (緑色のチェック マーク "✓" によって示されている)。 最後に、StringLibrary 内の他のメソッドにはコード カバレッジが設定されていないことがわかります (青い線 "➖" によって示されている)。

Live Unit Testing の開始後の Live Test Explorer とコード エディター ウィンドウ

Live Unit Testing の開始後の Live Test Explorer とコード エディター ウィンドウ

また、コード エディター ウィンドウ内の特定のコード カバレッジ アイコンを選択することにより、テスト カバレッジとテスト結果に関する詳細情報を取得することもできます。 この詳細を調べるには、次の手順を実行します。

  1. StartsWithUpper メソッド内の if (String.IsNullOrWhiteSpace(s)) という行に表示された緑のチェック マークをクリックします。 次の図に示すように、Live Unit Testing では、そのコード行が 3 つのテストでカバーされており、いずれのテストも正常に実行されたことが示されています。

    if 条件付きステートメントのコード カバレッジ

    if 条件付きステートメントのコード カバレッジ

  2. StartsWithUpper メソッド内の return Char.IsUpper(s[0]) という行に表示された緑のチェック マークをクリックします。 次の図に示すように、Live Unit Testing では、そのコード行が 2 つのテストのみでカバーされており、いずれのテストも正常に実行されたことが示されています。

    Return ステートメントのコード カバレッジ

    Return ステートメントのコード カバレッジ

Live Unit Testing は、重大な問題として不完全なコード カバレッジを明らかにしています。 この問題は、次のセクションで取り上げます。

テスト カバレッジの展開

このセクションでは、単体テストを StartsWithLower メソッドに展開します。 展開を行っている間も、Live Unit Testing によって引き続きコードのテストが実行されます。

コード カバレッジを StartsWithLower メソッドに展開するには、次の手順を実行します。

  1. 次の 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}");
        }
    }
    
  2. 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}");
    
  3. ソース コードを変更すると、Live Unit Testing によって新しいテストと変更したテストが自動的に実行されます。 次の図に示すように、追加した 2 つのテストと変更した 1 つのテストを含むすべてのテストが成功しています。

    テスト カバレッジを展開した後の Live Test Explorer

    テスト カバレッジを展開した後の Live Test Explorer

  4. StringLibrary クラスのソース コードが含まれるウィンドウに切り替えます。 Live Unit Testing は、コード カバレッジが StartsWithLower メソッドに展開されていることを示します。

    StartsWithLower メソッドのコード カバレッジ

    StartsWithLower メソッドのコード カバレッジ

場合によっては、成功したテストがテスト エクスプローラーで、灰色で表示されることがあります。これは、テストが現在実行中であることを示すか、または最後に実行されてから、テストに影響する変更がコードに加えられていないために、テストが再度実行されなかったことを示します。

これまで、すべてのテストが成功しています。 次のセクションでは、テスト エラーを処理する方法について説明します。

テスト エラーの処理

このセクションでは、Live Unit Testing を使用してテスト エラーを識別し、トラブルシューティングを行い、対処する方法を説明します。 そのために、テスト カバレッジを HasEmbeddedSpaces メソッドに展開します。

  1. テスト ファイルに次のメソッドを追加します。

    [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}");
        }
    }
    
  2. 次の図に示すように、テストが実行されると、Live Unit Testing では TestHasEmbeddedSpaces メソッドが失敗したことが示されます。

    失敗したテストを示す Live Test Explorer

    失敗したテストを示す Live Test Explorer

  3. ライブラリ コードが表示されたウィンドウを選択します。 Live Unit Testing によって HasEmbeddedSpaces メソッドまでコード カバレッジが広げられています。 また、Live Unit Testing は、失敗したテストでカバーされている行に赤い "" を追加することで、テスト エラーを報告しています。

  4. HasEmbeddedSpacesメソッド シグネチャが含まれている行にマウス ポインターを合わせます。 次の図に示すように、Live Unit Testing では、メソッドが 1 つのテストでカバーされていることを知らせるヒントが表示されます。

    失敗したテストに関する Live Unit Testing の情報

    失敗したテストに関する Live Unit Testing の情報

  5. 失敗した TestHasEmbeddedSpaces テストを選択します。 次の図に示すように、Live Unit Testing には、すべてのテストの実行やすべてのテストのデバッグなど、いくつかのオプションが用意されています。

    テストが失敗した場合のための Live Unit Testing のオプション

    テストが失敗した場合のための Live Unit Testing のオプション

  6. [すべてデバッグ] を選択して、失敗したテストをデバッグします。

  7. Visual Studio はデバッグ モードでテストを実行します。

    テストでは、配列内の各文字列が phrase という名前の変数に割り当てられ、その変数が HasEmbeddedSpaces メソッドに渡されます。 アサーション式が初めて false になると、プログラムの実行は一時停止し、デバッガーが呼び出されます。 Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue のメソッド呼び出しに予期しない値が含まれていると、以下の図に示すような例外ダイアログが表示されます。

    Live Unit Testing の例外ダイアログ

    Live Unit Testing の例外ダイアログ

    さらに、次の図に示すように、Visual Studio で用意されているすべてのデバッグ ツールを使用することで、失敗したテストのトラブルシューティングを効果的に行うことができます。

    Visual Studio のデバッグ ツール

    Visual Studio のデバッグ ツール

    [自動変数] ウィンドウで、phrase 変数の値が "Name\tDescription" (配列の 2 番目の要素) になっていることに注目してください。 テスト メソッドは、この文字列が HasEmbeddedSpaces に渡された場合に true が返されるのを期待していますが、実際に返されるのは false です。 タブ文字である "\t" が埋め込みスペースとして認識されていないのは明らかです。

  8. テスト プログラムの実行を続行するには、[デバッグ]>[続行] の順に選択するか、F5 キーを押すか、あるいはツールバーの [続行] ボタンをクリックします。 ハンドルされない例外が発生したため、テストは終了します。 これは、バグの予備調査を行う上で十分な情報です。 TestHasEmbeddedSpaces (テスト ルーチン) で不適切な想定が行われたか、または HasEmbeddedSpaces がすべての埋め込みスペースを正しく認識していません。

  9. 問題を診断し解決するには、StringLibrary.HasEmbeddedSpaces メソッドから始めます。 HasEmbeddedSpaces メソッドでの比較を確認します。 このメソッドでは、埋め込みスペースを U+0020 と見なしています。 ただし、Unicode Standard には、その他にも多くの空白文字が含まれています。 このことは、空白文字かどうかのテストが、ライブラリ コードで正しく行われていないことを示しています。

  10. 等価比較を、System.Char.IsWhiteSpace メソッドへの呼び出しに置き換えます。

    if (Char.IsWhiteSpace(ch))
    
  11. 失敗したテスト メソッドは、Live Unit Testing によって自動的に再実行されます。

    更新された結果は、Live Unit Testing とコード エディター ウィンドウに表示されます。