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

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

前提条件

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

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

  1. Visual Studio を起動します。

  2. Visual Studio を使用して .NET クラス ライブラリを作成する」で作成した ClassLibraryProjects ソリューションを開きます。

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

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

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

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

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

    5. [追加情報] ページの [フレームワーク] ボックスの [.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# では、名前空間は GlobalUsings.csglobal 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 で単体テストを実行すると、TestClassAttribute 属性でマークされたクラス内で、TestMethodAttribute 属性でマークされた各メソッドが実行されます。 テスト メソッドは、最初の失敗が検出されたとき、またはそのメソッドに含まれているすべてのテストが成功したときに終了します。

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

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

また、テスト メソッドで Assert.ThrowsException メソッドを使用して、スローすることが期待される例外の種類を示すこともできます。 指定した例外がスローされない場合、テストは失敗します。

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

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

メソッドを 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,
                           string.Format("Expected for '{0}': true; Actual: {1}",
                                         word, 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,
                           string.Format("Expected for '{0}': false; Actual: {1}",
                                         word, 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,
                           string.Format("Expected for '{0}': false; Actual: {1}",
                                         word == null ? "<null>" : word, 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 Save File As dialog

  3. [保存の確認] ダイアログで [はい] ボタンを選択してファイルを保存します。

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

    Visual Studio Advanced Save Options dialog

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

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

    Test Explorer window with passing tests

テストの失敗の処理

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

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

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

    Test Explorer window with failing tests

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

    [テスト エクスプローラー] ウィンドウに、アサートによって生成されたメッセージ "Assert.IsFalse failed. Expected for 'Error': false; actual:True" が表示されます。 エラーのため、配列内の "Error" の後ろの文字列はテストされませんでした。

    Test Explorer window showing the IsFalse assertion failure

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

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

これで、ライブラリのデバッグ ビルドを実行したときにすべてのテストが成功したので、さらにライブラリのリリース ビルドに対してテストを行います。 コンパイラの最適化などのさまざまな要因により、デバッグ ビルドとリリース ビルドとでは動作が異なる場合があります。

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

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

    Visual Studio toolbar with release build highlighted

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

    StringLibrary context menu with build command

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

テストのデバッグ

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

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

その他の技術情報

次の手順

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

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

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

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

前提条件

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

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

  1. Visual Studio を起動します。

  2. Visual Studio を使用して .NET クラス ライブラリを作成する」で作成した ClassLibraryProjects ソリューションを開きます。

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

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

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

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

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

    5. [追加情報] ページの [フレームワーク] ボックスの [.NET 7 (標準期間サポート)] を選択します。 次に、 [作成] を選択します。

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

    using Microsoft.VisualStudio.TestTools.UnitTesting;
    
    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
    

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

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

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

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

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

  2. [参照マネージャー] ダイアログで、[プロジェクト] ノードを展開し、[StringLibrary] の横のボックスをオンにします。 StringLibrary アセンブリに参照を追加すると、コンパイラが StringLibraryTest プロジェクトをコンパイル中に、StringLibrary メソッドを見つけることができるようになります。

  3. [OK] を選択します。

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

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

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

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

また、テスト メソッドで Assert.ThrowsException メソッドを使用して、スローすることが期待される例外の種類を示すこともできます。 指定した例外がスローされない場合、テストは失敗します。

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

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

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

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

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

    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,
                           string.Format("Expected for '{0}': true; Actual: {1}",
                                         word, 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,
                           string.Format("Expected for '{0}': false; Actual: {1}",
                                         word, 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,
                           string.Format("Expected for '{0}': false; Actual: {1}",
                                         word == null ? "<null>" : word, 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 Save File As dialog

  3. [保存の確認] ダイアログで [はい] ボタンを選択してファイルを保存します。

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

    Visual Studio Advanced Save Options dialog

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

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

    Test Explorer window with passing tests

テストの失敗の処理

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

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

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

    Test Explorer window with failing tests

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

    [テスト エクスプローラー] ウィンドウに、アサートによって生成されたメッセージ "Assert.IsFalse failed. Expected for 'Error': false; actual:True" が表示されます。 エラーのため、配列内の "Error" の後ろの文字列はテストされませんでした。

    Test Explorer window showing the IsFalse assertion failure

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

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

これで、ライブラリのデバッグ ビルドを実行したときにすべてのテストが成功したので、さらにライブラリのリリース ビルドに対してテストを行います。 コンパイラの最適化などのさまざまな要因により、デバッグ ビルドとリリース ビルドとでは動作が異なる場合があります。

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

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

    Visual Studio toolbar with release build highlighted

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

    StringLibrary context menu with build command

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

テストのデバッグ

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

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

その他の技術情報

次の手順

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

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

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

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

前提条件

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

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

  1. Visual Studio を起動します。

  2. Visual Studio を使用して .NET クラス ライブラリを作成する」で作成した ClassLibraryProjects ソリューションを開きます。

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

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

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

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

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

    5. [追加情報] ページの [フレームワーク] ボックスの [.NET 6 (長期的なサポート)] を選択します。 次に、 [作成] を選択します。

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

    using Microsoft.VisualStudio.TestTools.UnitTesting;
    
    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
    

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

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

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

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

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

  2. [参照マネージャー] ダイアログで、[プロジェクト] ノードを展開し、[StringLibrary] の横のボックスをオンにします。 StringLibrary アセンブリに参照を追加すると、コンパイラが StringLibraryTest プロジェクトをコンパイル中に、StringLibrary メソッドを見つけることができるようになります。

  3. [OK] を選択します。

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

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

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

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

また、テスト メソッドで Assert.ThrowsException メソッドを使用して、スローすることが期待される例外の種類を示すこともできます。 指定した例外がスローされない場合、テストは失敗します。

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

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

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

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

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

    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,
                           string.Format("Expected for '{0}': true; Actual: {1}",
                                         word, 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,
                           string.Format("Expected for '{0}': false; Actual: {1}",
                                         word, 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,
                           string.Format("Expected for '{0}': false; Actual: {1}",
                                         word == null ? "<null>" : word, 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 Save File As dialog

  3. [保存の確認] ダイアログで [はい] ボタンを選択してファイルを保存します。

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

    Visual Studio Advanced Save Options dialog

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

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

    Test Explorer window with passing tests

テストの失敗の処理

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

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

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

    Test Explorer window with failing tests

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

    [テスト エクスプローラー] ウィンドウに、アサートによって生成されたメッセージ "Assert.IsFalse failed. Expected for 'Error': false; actual:True" が表示されます。 エラーのため、配列内の "Error" の後ろの文字列はテストされませんでした。

    Test Explorer window showing the IsFalse assertion failure

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

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

これで、ライブラリのデバッグ ビルドを実行したときにすべてのテストが成功したので、さらにライブラリのリリース ビルドに対してテストを行います。 コンパイラの最適化などのさまざまな要因により、デバッグ ビルドとリリース ビルドとでは動作が異なる場合があります。

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

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

    Visual Studio toolbar with release build highlighted

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

    StringLibrary context menu with build command

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

テストのデバッグ

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

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

その他の技術情報

次の手順

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

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

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