Visual Studioでテストを行う方法

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

メソッドからテストを作成するときに、自動的にプロジェクトを作成することもできます。

[追加]→[新しいプロジェクト]から、テストプロジェクトを作成します。

そうすると、次のようなテストのひな形となるクラスが作成されます。

using System;
using System.Text;
using System.Collections.Generic;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace TestProject1
{
    /// <summary>
    /// UnitTest1 の概要の説明
    /// </summary>
    [TestClass]
    public class UnitTest1
    {
        public UnitTest1()
        {
            //
            // TODO: コンストラクタ ロジックをここに追加します
            //
        }

        private TestContext testContextInstance;

        /// <summary>
        /// 現在のテストの実行についての情報および機能を
        /// 提供するテスト コンテキストを取得または設定します。
        /// </summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region 追加のテスト属性
        //
        // テストを作成するときには、次の追加属性を使用できます:
        //
        // クラス内で最初のテストを実行する前に、ClassInitialize を使用してコードを実行してください
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // クラス内のテストをすべて実行したら、ClassCleanup を使用してコードを実行してください
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // 各テストを実行する前に、TestInitialize を使用してコードを実行してください
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // 各テストを実行した後に、TestCleanup を使用してコードを実行してください
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        [TestMethod]
        public void TestMethod1()
        {
            //
            // TODO: テスト ロジックをここに追加してください
            //
        }
    }
}

また次の2つのファイルが作成され、プロジェクトに追加されます。

  • SolutionName.vsmdi (テストのメタデータ)
  • LocalTestRun.testrunconfig (ローカルでのテストの実行構成)

これらはソリューション エクスプローラ上では、[Solution Items]というフォルダに格納される形で表示されます。

テストの作成

メソッドからテストを作成

たとえば、

public int Add( int a, int b )
{
    return a + b;
}

のようなメソッドがあるとします。このメソッドのテストを作成するには、メソッド内で右クリックをして[単体テストの作成]を選択します。

対象とするメソッドを含むクラスの一覧が表示され、テスト対象のクラスにはチェックが入ります。確認したら[OK]をクリックします。

そうすると、次のようなテストのコードが作成されます。

/// <summary>
/// Add のテスト
/// </summary>
[TestMethod()]
public void AddTest()
{
    Class1 target = new Class1(); // TODO: 適切な値に初期化してください

    int a = 0;        // TODO: 適切な値に初期化してください
    int b = 0;        // TODO: 適切な値に初期化してください
    int expected = 0; // TODO: 適切な値に初期化してください

    int actual;
    actual = target.Add( a, b );

    Assert.AreEqual( expected, actual );

    Assert.Inconclusive( "このテストメソッドの正確性を確認します。" );
}

まずこの時点でテストを実行して、テストが失敗することを確認します。そしてコメントで「TODO」の記述がある箇所にテストの具体的な内容を記述し、テストを実装します。最後にInconclusive()メソッドの呼び出しを削除し、テストの実装を完了します。

internalクラスのテスト

internalと指定されたクラスに対してテストを作成しようとすると、[InternalsVisibleTo 属性を追加する]ダイアログが表示されます。

これは外部のアセンブリであるテストのプロジェクトから、テスト対象のプロジェクトの内部へアクセスできるようにするための設定です。InternalsVisibleTo 属性の設定 | MSDN

ただしアセンブリ内部へのアクセスは次項のpublicizeでも行えるため、privateなメンバもテスト対象とするならば[いいえ]を選択し属性の追加を行いません。逆にprivateなメンバを含まないならば[はい]を選択します。

ダイアログが表示されない場合にはテスト対象のプロジェクトに、次のようにInternalsVisibleTo属性を記述します。この指定は1か所に記述すれば、namespaceが同一の他のinternalなクラスにも適用されます。

[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("TestProjectName")]
namespace Sample
{
    internal class MyClass
    {
    }
}
第7回 名前空間のエイリアス修飾子と外部アセンブリ - @IT 川俣晶 (2007/11/30)

この指定がないと対象のアセンブリへはアクセスできないため、「error CS0122: '***' はアクセスできない保護レベルになっています」としてエラーとなります。

privateメンバのテスト

アクセサによる方法はVisual Studio 2010で非推奨となり、それ以降のバージョンでは「単体テストの作成は、パブリック クラスかパブリック メソッド内でのみサポートされます。(Create Unit Tests is supported only within a public class or a public method.)」として、以下の方法は適用できなくなっています。よってType.GetMethod()から取得したオブジェクトを介して呼び出します。

privateメンバのテストを作成すると、そのメンバにアクセスするために次のようなProjectName.accessorファイルが作成されます。その内容はアセンブリ名と、実行プラットフォームを特定するためのオプションの指定です。publicize を使用したプライベート アクセサの作成 | MSDN

AssemblyName
Desktop

またテストのプロジェクトファイル (.csproj) にはそのファイルを参照するように、次のコードが追加されます。

<ItemGroup>
  <Shadow Include="テストの参照\ProjectName.accessor" />
</ItemGroup>

以降はClassName_Accessorの形式のクラス名でクラスを作成することで、そのインスタンスからはそのクラスのprivateメンバにアクセスできるようになります。

ClassName_Accessor testClass = new ClassName_Accessor();

テストからメソッドを作成 (メソッドスタブの生成)

先に作成したテストから、テスト対象のメソッドを作成する方法について解説します。これによりテストファーストを実践することになります。

まずはテストのコードを記述します。このときまだ実装されていないテスト対象のメソッドが、あたかも存在するかのように記述の要す。

[TestMethod]
public void TestMethod1()
{
    Class1 target = new Class1();

    int actual = target.Sub( 3, 2 );
    Assert.AreEqual( 1, actual );
}

実装していないメソッドを記述すると、修正候補に[~のメソッド スタブを生成します。]と表示されるので、それをクリックします。

またはメニューから【編集 → IntelliSense → メソッド スタブの生成】としてもメソッド スタブを生成できます。

メソッド呼び出し時のコードが解析され、引数や戻り値の型も正確に、テスト対象のクラスにメソッドが作成されます。

public int Sub( int p, int p_2 )
{
    throw new NotImplementedException();
}

プロジェクトで参照されていないファイルの追加

プロジェクトで参照されているファイルはテストの実行環境へも自動でコピーされますが、参照されていないファイルは個別にコピーされるように設定する必要があります。

メニューの【テスト → テストの実行構成の編集 → ローカル テストの実行】から[localtestrun.testrunconfig]のウィンドウを開きます。そして[配置]で追加するファイルを設定します。

テストの実行

テストはエディタ上か、もしくはテストビューなどのウィンドウ上から実行できます。

エディタ上から実行

実行したいテスト上で右クリックし、[テストの実行]をクリックします。

テストメソッド上で右クリックした場合にはそのメソッドだけが、テストクラス上ならばそのクラスのすべてのメソッドのテストが実行されます。

ウィンドウ上から実行

テストビュー

テストビューでテストをクリックして選択し、[選択範囲の実行]ボタンをクリックして実行します。複数のテストを実行するには、ShiftキーやCtrlキーを押しながらテストをクリックして選択します。

テスト リスト エディタ

テストのチェックボックスにチェックを入れ、[選択されたテストを実行]ボタンをクリックして実行します。

テスト エクスプローラー

並列テスト (parallel test)

ツールバーにある[テストを並列で実行する]ボタンを有効にすることで、複数のテストを同時に実行できるようになります。

トラブル対処法
何も表示されない

Visual Studio 2015において「パーツ "Microsoft.VisualStudio.TestWindow.UI.TestWindowToolWindowControl" の初期化中に例外がスローされました。」としてテスト エクスプローラーに何も表示されないときには、Visual Studioの言語設定を英語に変更するかコマンドラインから実行します。Visual Studio 2015 "日本語" の環境に最新の更新 (KB3165756) を適用するとテストエクスプローラーが機能しなくなります

この問題は、Update for Microsoft Visual Studio 2015 Update 3 (KB3165756)を適用することで解決できます。

概要部分が表示されない

個々のテストの概要部分が表示されないときには、すべてのテストを折りたたんでみます。

テスト結果の確認

[テスト結果]ウィンドウで、テストの結果を確認できます。

テストが失敗したときのエラーメッセージはToString()メソッドの内容で表示されますので、独自に作成した型はToString()メソッドをオーバーライドするようにします。

テストの保存と削除

実行したテストの結果はすべて保存され、この保存される数の上限が、既定で25となっています。テストの実行数がこの上限を超えると実行時に、


古いテスト結果の削除確認ダイアログ

のようなダイアログが表示され、テスト結果の削除の確認を求められます。ここで[OK]をクリックすると、最近の4つの結果以外を削除してテストを実行します。[キャンセル]を選択すると削除せず、テストも実行されません。

このダイアログは[今後このダイアログ ボックスを表示しない]にチェックを入れることで以降、表示されないようにできます。再び表示したい場合は、オプションの【テスト ツール → 既定のダイアログ ボックス動作】にある[最大数を超えた古いテスト実行結果を削除する場合]の設定を[常にメッセージを表示する]に変更します。

保存されるテスト数の変更

オプションの【テスト ツール → テストの実行】にある[古いテスト結果の最大数]を変更することで、保存される数の上限を変更できます。保存されるテストの実行の数を制限する | MSDN

テストの設定

コマンドラインでのテスト

テストのプロジェクトをビルドし、アセンブリ (.dll) を作成します。そして[開発者コマンド プロンプト (VS**** コマンド プロンプト)]からコマンドプロンプトを起動し、MSTest.exeでそれを実行します。

C:\mstest /testcontainer:UnitTest.dll

テスト結果の詳細は、TestResultsフォルダに*.trxの拡張子で保存されます。これを任意のファイルにするには/resultsfileオプションで指定します。

C:\mstest /testcontainer:UnitTest.dll /resultsfile:results.trx

ただしこのようにファイルを明示した場合、再実行時には「結果ファイル "**.trx" は既に存在します。異なる結果ファイルを指定するか、既存のファイルが必要でないことを確認して削除してください。」として実行に失敗します。この問題には、指示通りファイルを削除するしかないようです。Any command-line option to overwrite existing resultsfile when same test is run using MSTest?

指定テストのみの実行

/testオプションで、テスト対象を限定できます。/test - MSTest.exe command-line options | MSDN

C:\mstest /testcontainer:UnitTest.dll /test:TestMethod1

このときテスト名は部分一致で解釈されるため、たとえば

C:\mstest /testcontainer:UnitTest.dll /test:ittest

とすると、この文字列に一致する

  • TestProject.UnitTest1.TestMethod1
  • TestProject.UnitTest1.TestMethod2

のような名前を持つ、すべてのテストが実行されます。

トラブル対処法

テストに失敗し*.trxファイルに「System.IO.FileNotFoundException: ファイルまたはアセンブリ '**.dll'、またはその依存関係の 1 つが読み込めませんでした。指定されたモジュールが見つかりません。」と記録されるときには、その.dllがあるフォルダにpathを通します。

その他

UIのテスト

コード化されたUIテスト (自動UIテスト) の利用には、以下のエディションが必要です。

  • Visual Studio 2010 … Ultimate、Premium、Test Professional
  • Visual Studio 2012 … Ultimate、Premium
  • Visual Studio 2013 … Ultimate、Premium
  • Visual Studio 2015 … Enterprise

継続的インテグレーション (Continuous Integration : CI)

CIの利用にはVisual Studio Team Services (Visual Studio Online) で、Gitなどによりソースが管理されている必要があります。

トラブル対処法

ビルドに失敗する

既定ではテストのプロセス (vstest.executionengine) は実行後に終了しないため、関連するファイルがロックされることで上書きできず、ビルドに失敗することがあります。これはメニューの【テスト → テスト設定 → テスト実行エンジンの実行状態を保持する (Keep Test Execution Engine Running)】のチェックを外すことで解決できます。vstest.executionengine - unit testing - vstest.executionengine.x86.exe not closing - Stack Overflow

テストが検出されない

作成したテストが検出されず、テスト エクスプローラーに「ソリューションをビルドして、使用可能なテストをすべて検出します。[すべて実行] をクリックして、ソリューションのすべてのテストをビルド、探索、および実行します。」とだけ表示されることがあります。

この場合にはソリューションとテストで、対象とするプラットフォームが一致しているか確認します。テストのそれはメニューの【テスト → テスト設定 → 既定のプロセッサ アーキテクチャ】で確認でき、この指定がソリューション プラットフォームと一致していないと、テストは検出されません。Visual Studio 2015 or 2017 does not discover unit tests - Stack Overflow

対象とするプラットフォームを変更したことで一致しなくなると、テスト エクスプローラーの個別のテストに「ソース:利用可能なソースがありません (Source:no source available)」と表示されソースを開けなくなり、ソースの側からも実行できなくなります。

テスト メソッドの要件を満たしていない

テスト対象とするメソッドには引数や戻り値に制約があり、それらを満たしていないとテストとして検出されません。

テストが実行されない

テスト成果が[スキップ]となっている場合には、テストメソッドがIgnore属性により無効化されていないか確認します。

実行するテストが見つからない

テストを実行しようとしたときに、出力ウィンドウに「実行するテストが見つかりませんでした。」と表示されるときには、%TEMP%\VisualStudioTestExplorerExtensionsを削除してから再試行してみます。Visual Studio 2015 or 2017 does not discover unit tests - Stack Overflow

テストをデバッグできない

テストをデバッグしようとすると「通信オブジェクト System.ServiceModel.Channels.WindowsStreamSecurityUpgradeProvider は Opening 状態です。通信オブジェクトは、Opened 状態でないと通信に使用できません。」としてInvalidOperationExceptionが投げられることがあります。

テストが中止される

テストの実行時、「実行プロセスが予期せず終了したため、アクティブなテストの実行が中止されました。(The active Test Run was aborted because the execution process exited unexpectedly.) 詳しく調査するには、ローカル クラッシュ ダンプをコンピューター レベルまたはプロセス vstest.executionengine.x86.exe で有効にします。詳細については、ここを参照してください: http://go.microsoft.com/fwlink/?linkid=232477」として、途中で中止されることがあります。

指示に従いクラッシュ ダンプを有効にするには、レジストリエディタで[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting]に、LocalDumpsの名前でキーを作成します。既定ではミニ ダンプとなっているため、より詳細な情報が必要ならばDWORDで"DumpType"の値を作成し、2 (完全ダンプ) を指定します。ユーザーモード ダンプの収集 - Win32 apps | Microsoft Learn

クラッシュ ダンプを有効にすると、同じ状況となったときにはそれを開いてデバッグするように促されるようになります。このダンプ ファイルは、既定で%LOCALAPPDATA%\CrashDumpsに作成されます。

  • 例外により終了した場合にもこのようにテストが中止されるため、捕捉されていない例外がないかコードを確認する。
  • C#からC++のライブラリを利用しているような場合には、[ネイティブ コードのデバッグを有効にする]を有効にすることで、問題の詳細を確認できることがある。

作業ディレクトリの設定が適用されない

プロジェクトのプロパティで設定できるデバッグ時の作業ディレクトリは、テストのデバッグでは適用されません。よってその必要があるならば、コード上で指定します。

ImageDirectoryEntryToData failed

========== テスト検出を開始しています ==========
Test Adapter for Google Test: Test discovery starting...
ERROR: ImageDirectoryEntryToData failed!
Test discovery completed, overall duration: **:**:**.*******
ERROR:
================
The following errors and warnings have occured during test discovery  (enable debug mode for more information):
ERROR: ImageDirectoryEntryToData failed!
ImageDirectoryEntryToData が失敗しました。
Microsoft Learnから検索