次の方法で共有


チュートリアル: Visual Studio を使用して .NET で .NET クラス ライブラリをテストする

このチュートリアルでは、テスト プロジェクトをソリューションに追加して、単体テストを自動化する方法について説明します。

必須コンポーネント

このチュートリアルでは、「Visual Studio を使用して .NET クラス ライブラリを作成する」で作成するソリューションを使用します。

単体テスト プロジェクトを作成する

単体テストでは、開発および公開時に自動化されたソフトウェア テストが提供されます。 MSTest は、選択できる 3 つのテスト フレームワークのうちの 1 つです。 その他は xUnitnUnitです。

  1. Visual Studio を起動します。

  2. ClassLibraryProjects」で作成した ソリューションを開きます。

  3. 「StringLibraryTest」 という名前の新しい単体テスト プロジェクトをソリューションに追加します。

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

    2. [新しいプロジェクトの追加] ページで、検索ボックスに「mstest」と入力します。 言語の一覧から [C#] または [Visual Basic] を選択し、次に、プラットフォームの一覧から [すべてのプラットフォーム] を選択します。

    3. MSTest テスト プロジェクト テンプレートを選択し、[次へ] を選択します。

    4. [新しいプロジェクトの構成] ページで、[プロジェクト名] ボックスに「StringLibraryTest」と入力します。 [次へ] を選びます。

    5. [追加情報] ページで、[Framework] ボックスの中から .NET 8 を選択します。 次に、[の作成] を選択します。

  4. Visual Studio によってプロジェクトが作成され、クラス ファイルが次のコードでコード ウィンドウに開かれます。 使用する言語が表示されない場合は、ページの上部にある言語セレクターを変更します。

    namespace StringLibraryTest;
    
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
        }
    }
    
    Imports Microsoft.VisualStudio.TestTools.UnitTesting
    
    Namespace StringLibraryTest
        <TestClass>
        Public Class UnitTest1
            <TestMethod>
            Sub TestSub()
    
            End Sub
        End Class
    End Namespace
    

    単体テストのテンプレートで作成されたソース コードにより、次の処理が行われます。

    • 単体テストに使用される型を含む Microsoft.VisualStudio.TestTools.UnitTesting 名前空間をインポートします。 C# では、global using ディレクティブを介して名前空間がインポートされます。
    • TestClassAttribute クラスにUnitTest1属性を適用します。
    • TestMethodAttribute属性を適用して、C# でTestMethod1を定義するか、Visual Basic でTestSubを定義します。

    [TestClass] でタグ付けされたテスト クラスの [TestMethod] でタグ付けされた各メソッドは、単体テストの実行時に自動的に実行されます。

プロジェクト参照を追加する

テスト プロジェクトが StringLibrary クラスと連動するように、StringLibraryTest プロジェクトに StringLibrary プロジェクトへの参照を追加します。

  1. ソリューション エクスプローラー[StringLibraryTest] プロジェクトの [依存関係] ノードを右クリックし、コンテキスト メニューの [プロジェクト参照の追加] を選択します。

  2. [参照マネージャー] ダイアログボックスで、[プロジェクト] ノードを展開し、[StringLibrary] の横にあるボックスを選択します。 StringLibrary アセンブリへの参照を追加すると、コンパイラは StringLibraryTest プロジェクトのコンパイル中に StringLibrary メソッドを検索できます。

  3. OK を選択します。

単体テスト メソッドの追加と実行

Visual Studio では、単体テストを実行すると、 TestMethodAttribute 属性でマークされた各メソッドが、 TestClassAttribute 属性でマークされたクラスで実行されます。 テスト メソッドは、最初のエラーが見つかったか、メソッドに含まれるすべてのテストが成功したときに終了します。

一般的なテストでは、Assert クラスのメンバーが呼び出されます。 多くのアサート メソッドは最低 2 つのパラメーターを含んでいます。1 つは予期されるテスト結果、もう 1 つは実際のテスト結果です。 次の表に、Assert クラスの頻繁に呼び出されるメソッドをいくつか示します。

Assert メソッド Function
Assert.AreEqual 2 つの値または 2 つのオブジェクトが等しいことを確認します。 値またはオブジェクトが等しくない場合、アサートは失敗します。
Assert.AreSame 2 つのオブジェクト変数が同じオブジェクトを参照していることを確認します。 変数が別々のオブジェクトを参照している場合、アサートは失敗します。
Assert.IsFalse 条件がfalseであることを検証します。 条件が true の場合、アサートは失敗します。
Assert.IsNotNull オブジェクトが null でないことを確認します。 オブジェクトが null である場合、アサートは失敗します。

また、テスト メソッドで Assert.ThrowsException (MSTest 3.8 以降を使用している場合は Assert.Throws および Assert.ThrowsExactly) メソッドを使用して、スローする必要がある例外の種類を示すこともできます。 指定した例外がスローされない場合、テストは失敗します。

StringLibrary.StartsWithUpper メソッドのテストでは、大文字で始まる多くの文字列を指定する必要があります。 これらの場合ではメソッドが true を返すと予測されるので、Assert.IsTrue メソッドを呼び出すことができます。 同様に、大文字以外の文字列で始まる文字列を多数指定する必要があります。 これらの場合ではメソッドが false を返すと予測されるので、Assert.IsFalse メソッドを呼び出すことができます。

ライブラリ メソッドは文字列を処理するため、空の文字列 (String.Empty) (文字がなく Length が 0 である有効な文字列)、および初期化されていない null 文字列を正しく処理することも確認します。 StartsWithUpperを静的メソッドとして直接呼び出し、1 つのString引数を渡します。 または、StartsWithUpperに割り当てられたstring変数の拡張メソッドとしてnullを呼び出します。

3 つのメソッドを定義します。各メソッドは、文字列配列内の各要素に対して Assert メソッドを呼び出します。 テストエラーが発生した場合に表示するエラー メッセージを指定できるメソッド オーバーロードを呼び出します。 このメッセージによって、エラーの原因となった文字列が識別されます。

テスト メソッドを作成するには

  1. UnitTest1.cs または UnitTest1.vb コード ウィンドウで、コードを次のコードに置き換えます。

    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>"}': false; Actual: {result}");
            }
        }
    }
    
    Imports Microsoft.VisualStudio.TestTools.UnitTesting
    Imports UtilityLibraries
    
    Namespace StringLibraryTest
        <TestClass>
        Public Class UnitTest1
            <TestMethod>
            Public Sub TestStartsWithUpper()
                ' Tests that we expect to return true.
                Dim words() As String = {"Alphabet", "Zebra", "ABC", "Αθήνα", "Москва"}
                For Each word In words
                    Dim result As Boolean = word.StartsWithUpper()
                    Assert.IsTrue(result,
                           $"Expected for '{word}': true; Actual: {result}")
                Next
            End Sub
    
            <TestMethod>
            Public Sub TestDoesNotStartWithUpper()
                ' Tests that we expect to return false.
                Dim words() As String = {"alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                                   "1234", ".", ";", " "}
                For Each word In words
                    Dim result As Boolean = word.StartsWithUpper()
                    Assert.IsFalse(result,
                           $"Expected for '{word}': false; Actual: {result}")
                Next
            End Sub
    
            <TestMethod>
            Public Sub DirectCallWithNullOrEmpty()
                ' Tests that we expect to return false.
                Dim words() As String = {String.Empty, Nothing}
                For Each word In words
                    Dim result As Boolean = StringLibrary.StartsWithUpper(word)
                    Assert.IsFalse(result,
                           $"Expected for '{If(word Is Nothing, "<null>", word)}': false; Actual: {result}")
                Next
            End Sub
        End Class
    End Namespace
    

    TestStartsWithUpper メソッドの大文字のテストには、ギリシャ語の大文字のアルファ (U+0391) とキリル文字の大文字 EM (U+041C) が含まれています。 TestDoesNotStartWithUpper メソッドの小文字のテストには、ギリシャ語の小文字のアルファ (U+03B1) とキリル文字の小文字 Ghe (U+0433) が含まれています。

  2. メニュー バーで、[ファイル]>[名前を付けて UnitTest1.cs を保存] または [ファイル]>[名前を付けて UnitTest1.vb を保存] の順に選択します。 [名前を付けてファイルを保存] ダイアログで、[保存] ボタンの横にある矢印を選択して、[エンコード付きで保存] を選択します。

    Visual Studio の「名前を付けてファイルを保存」ダイアログ

  3. [名前を付けて保存の確認] ダイアログ ボックスで、[はい] ボタンを選択してファイルを保存します。

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

    Visual Studio の [保存オプションの詳細設定] ダイアログ

    ソース コードを UTF8 でエンコードされたファイルとして保存できない場合、Visual Studio によって ASCII ファイルとして保存される可能性があります。 その場合、ランタイムは ASCII 範囲外の UTF8 文字を正確にデコードせず、テスト結果が正しくありません。

  5. メニュー バーで [テスト]>[すべてのテストを実行する] を選択します。 テスト エクスプローラーの ウィンドウが開かない場合は、[テスト>テスト エクスプローラー]を選んで開いてください。 3 つのテストが [成功したテスト] セクションに表示され、[概要] セクションにはテストの実行結果が表示されています。

    テストが合格したテスト エクスプローラー ウィンドウで [テスト エクスプローラー] ウィンドウと成功したテスト

テストの失敗の処理

テスト駆動開発 (TDD) を行っている場合、最初にテストを作成すると、1 回目のテスト実行は失敗します。 その後、テストを成功させるコードをアプリに追加します。 このチュートリアルでは、検証するアプリ コードを記述した後にテストを作成したため、テストが失敗することはありませんでした。 テストの失敗が予想されるときにテストが失敗することを検証するには、テスト入力に無効な値を追加します。

  1. words メソッドの TestDoesNotStartWithUpper 配列を変更し、文字列 "Error" を含めます。 ソリューションをビルドし、テストを実行するときに、Visual Studio では、自動的に開いているファイルが保存されるため、ファイルは保存する必要はありません。

    string[] words = { "alphabet", "Error", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                       "1234", ".", ";", " " };
    
    Dim words() As String = { "alphabet", "Error", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                       "1234", ".", ";", " " }
    
    
  2. メニューバーから テスト>すべてのテスト を選択して、テストを実行します。 [テスト エクスプローラー] ウィンドウに、テストが 2 つ成功し、1 つ失敗したことが示されます。

    [テスト エクスプローラー] ウィンドウで失敗したテスト

  3. 失敗したテストを選択し、TestDoesNotStartWithを実行します。

    テスト エクスプローラーの ウィンドウには、assert によって生成されたメッセージ「Assert.IsFalse に失敗しました」が表示されます。 'Error' に期待される結果: false; 実際の結果: True。失敗により、"Error" の後の配列内の文字列はテストされませんでした。

    IsFalse アサーションの失敗を示す [テスト エクスプローラー] ウィンドウ

  4. 手順 1 で追加した文字列 "Error" を削除します。 テストを再実行すると、テストは成功します。

ライブラリのリリース バージョンのテスト

ライブラリのデバッグ ビルドを実行するときにテストがすべて成功したら、ライブラリのリリース ビルドに対してテストをもう一度実行します。 コンパイラの最適化を含む一部の要因により、デバッグ ビルドとリリース ビルドの間で動作が異なる場合があります。

リリース ビルドをテストするには、次の操作を行います。

  1. Visual Studio のツールバーで、ビルド構成を [デバッグ] から [リリース] に変更します。

    リリース ビルドが強調表示された Visual Studio ツールバー

  2. ソリューション エクスプローラーで [StringLibrary] プロジェクトを右クリックし、コンテキスト メニューの [ビルド] を選択し、ライブラリを再コンパイルします。

    ビルド コマンド を使用して StringLibrary コンテキスト メニューを する

  3. メニューバーから [テスト] を選び、[すべてのテストの実行]> を実行して、単体テストを行います。 テストが成功します。

テストのデバッグ

IDE として Visual Studio を使用する場合は、「チュートリアル: Visual Studio を使用して .NET コンソール アプリケーションをデバッグする」に示されているものと同じプロセスを使用し、単体テスト プロジェクトを使ってコードをデバッグできます。 ShowCase アプリ プロジェクトを開始する代わりに、StringLibraryTests プロジェクトを右クリックし、コンテキスト メニューから [テストのデバッグ] を選択します。

Visual Studio により、デバッガーがアタッチされた状態でテスト プロジェクトが開始されます。 テスト プロジェクトまたは基になるライブラリ コードに追加したブレークポイントで実行が停止します。

その他の技術情報

次のステップ

このチュートリアルでは、クラス ライブラリの単体テストを行いました。 NuGet にパッケージとして発行して、ライブラリを他のユーザーが使用できるようにします。 方法については、次の NuGet のチュートリアルに従ってください。

ライブラリを NuGet パッケージとして発行した場合、他のユーザーもそれをインストールして使用できます。 方法については、次の NuGet のチュートリアルに従ってください。

ライブラリはパッケージとして配布する必要はありません。 それが使用されるコンソール アプリにバンドルすることができます。 コンソール アプリを発行する方法については、このシリーズの前のチュートリアルを参照してください。