[C#, MSTest] ユニットテストのメモ

カタログ
  1. 1. Assertクラスのスタティックメソッドでテストを行う
  2. 2. 例外を投げるメソッドのテストはこんなメソッド作っとくと便利
  3. 3. privateメソッドはどうする?
    1. 3.1. 可視性をinternal にしてprivateメンバにアクセス
    2. 3.2. PrivateObject, PrivateType クラスを使う(MSTest)
    3. 3.3. リフレクション
    4. 3.4. Chaining Assertion
  4. 4. protectedメソッドはどうする?
  5. 5. テスト準備用のメソッドを作成するための属性
  6. 6. TestCategory テストカテゴリの分類
  7. 7. テスト時にリソースファイルをDeploymentItemで指定
    1. 7.1. 1. プロジェクトのテスト設定で配置を有効にする
    2. 7.2. 2. DeploymentItemでリソースを指定する

MsTestでユニットテストを書く場合はユニットテスト用のプロジェクトを作成し、 UnitTestFramework と テストしたい物 の参照を追加すればテストできる。

[テスト] -> [実行] -> [ソリューションのすべてのテスト] でテスト実行できる。

[追記 20151018] マルチプラットフォームで実行可能にしたい場合

プロジェクトの作成時に、「単体テストプロジェクト」 を選択するとMSTestによるテストプロジェクトが作られるが、 このプロジェクトはXamarin.Macで開けなかった。

Xamarin.Macでも開けるようにするため 「クラス ライブラリ」のプロジェクトを作成して、 以下のようにテストプロジェクトを編集

  • NuGetのpackages.configに NUnit を追加
  • [Visual Studio] Microsoft.VisualStudio.TestTools.UnitTestFramework の参照を追加

また Visual Studio に NUnit Test Adapter の拡張機能をインストールする。

テストコードを記述する際は、 以下の様に切り替えれば簡単にMSTEstとNUnitに両対応できる。#define よりプロジェクト設定のコンパイラーシンボルで切り替えたほうがいいかな?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define USING_NUNIT

#if USING_NUNIT
using NUnit.Framework;
using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
using TestMethodAttribute = NUnit.Framework.TestAttribute;
using ClassInitializeAttribute = NUnit.Framework.TestFixtureSetUpAttribute;
using ClassCleanupAttribute = NUnit.Framework.TestFixtureTearDownAttribute;
using TestInitializeAttribute = NUnit.Framework.SetUpAttribute;
using TestCleanupAttribute = NUnit.Framework.TearDownAttribute;
using TestCategoryAttribute = NUnit.Framework.CategoryAttribute;
#else
using Microsoft.VisualStudio.TestTools.UnitTesting;
#endif

TimeoutAttribute, DescriptionAttribute, IgnoreAttribute などは同名なので名前空間をインポートしていればそのまま使える。

[追記おわり]

Assertクラスのスタティックメソッドでテストを行う

  • Assert.AreEqual
  • Assert.AreNotEqual
  • Assert.AreSame
  • Assert.IsFalse
  • Assert.IsTrue
  • Assert.IsNull
  • Assert.Inconclusive
  • Assert.IsInstanceOfType

などなど。

例外を投げるメソッドのテストはこんなメソッド作っとくと便利

1
2
3
4
5
6
7
8
9
10
11
12
private E GetException<E>(Action action) where E : Exception
{
try
{
action();
return null;
}
catch (E ex)
{
return ex;
}
}

privateメソッドはどうする?

[この辺いろいろ追記・修正 20151018] 以下の方法がある

  • 可視性を internal にする
  • PrivateObject, PrivateType クラスを使う(MSTest)
  • リフレクション

可視性をinternal にしてprivateメンバにアクセス

テスト用プロジェクトからだとprivateメソッドにアクセスできないので…

  • テスト対象メソッドの private を internal に変更する
  • AssemblyInfo.cs に [assembly: System.Runtime.CompilerServices.InternalsVisibleTo(“テスト用プロジェクトで生成するアセンブリの名前”)] 属性をつける。記述するのはテスト対象となるプロジェクト内であり、テスト用のプロジェクトにはこの記述は必要ない。

これで テスト用のプロジェクトのメソッドに、テスト用のプロジェクトからアクセスできる。 ただし、この方法だと同じプロジェクトからはアクセスできてしまうという問題があるので、 同じプロジェクトからもアクセスされたくなければ別の方法を使うこと。

※以前は プライベートアクセサ を使用していたらしいけど Visual Studio 2012 では Deprecated 推奨されなくなった?

PrivateObject, PrivateType クラスを使う(MSTest)

使い方は簡単なので割愛。でもこれらのクラスはMSTestのものなのでNUnitの場合に困ると思われる。

リフレクション

NUnitでも問題のない方法ならリフレクションを使う。リフレクションの方法はググる。 でも実行がすごくメンドイのでPrivateメソッド呼び出す自作クラスInvokeUtility

Chaining Assertion

http://chainingassertion.codeplex.com/

ググろう。便利。

protectedメソッドはどうする?

internal protected にすれば継承しなくてもテストクラスから呼び出せる。

テスト準備用のメソッドを作成するための属性

以下は、Visual Basic 2010 で自動生成されたコードのほぼコピペ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//
//テストを作成するときに、次の追加属性を使用することができます:
//

[ClassInitialize()]
public static void MyClassInitialize(TestContext testContext)
{

// クラスの最初のテストを実行する前にコードを
// 実行するには、ClassInitialize を使用
}

[ClassCleanup()]
public static void MyClassCleanup()
{

// クラスのすべてのテストを実行した後に
// コードを実行するには、ClassCleanup を使用
}

[TestInitialize()]
public void MyTestInitialize()
{

// 各テストを実行する前にコードを
// 実行するには、TestInitialize を使用
}

[TestCleanup()]
public void MyTestCleanup()
{

// 各テストを実行した後にコードを
// 実行するには、TestCleanup を使用
}

AssemblyInitializeAttribute, AssemblyCleanupAttributeという属性もある。

TestCategory テストカテゴリの分類

1
2
3
4
5
6
[TestMethod()]
[TestCategory("カテゴリーF")]
public void てすと()
{
// do something...
}

テスト時にリソースファイルをDeploymentItemで指定

[追記 20-151018] なんかおかしい

テスト時にファイルを参照したい時のメモなんだけど…こんなことしなくてもいい。 テスト用にファイルを用意するのならテストプロジェクトにファイルを追加して、テストコードから相対パスでアクセスできる。

だったら DeploymentItemAttribute とはいったい何なのか。 MSDNによると「テスト実行前のアセンブリとともに配置する必要があるディレクトリまたはファイルを指定します。 」とのこと。 参考: .NET - MsTestによるユニットテストの解説 - Qiita

また、外部ファイルを利用する場合は DataSourceAttribute でCSVやXMLを利用できる。

参考: VS2008の単体テストにてテストデータを外部ファイルから設定する (Visual Studio, 単体テスト, CSV, XML) - いろいろ備忘録日記

[追記おわり]

1. プロジェクトのテスト設定で配置を有効にする

これをしないとDeploymentItemで配置するリソースを指定しても配置してくれない。

Visual Studio 2010 の [テスト] -> [テスト設定の編集] -> [ローカル(Local.testsettings)] でテストの設定を開いて [配置] -> [配置を有効にする] のチェックボックスをON

※テスト設定がない場合はプロジェクト…ではなく ソリューションにテスト設定のファイルを追加すること。

2. DeploymentItemでリソースを指定する

以下のようにコーディングする。

※Text.txtはプロパティで 「出力先ディレクトリにコピー : 常にコピーする」を指定する。

1
2
3
4
5
6
7
[TestMethod]
[DeploymentItem("Test.txt")]
public void Test()
{

string text = System.IO.File.ReadAllText("Text.txt");
Assert.AreEqual("http://minimalie.com", text);
}

※設定が正しいのにうまくいかなかったら Visual Studio を再起動するとうまくいくかも。

関連があるかもしれない記事