Visual Studio上 で プライベートなメンバやメソッドなど非公開メンバの単体テストに対して実施する場合、 リフレクションを使用する代わりにPrivateObject, PrivateTypeを使用できます。今回は単体テストプロジェクトでPrivateObject,PrivateType を使用して、staticなメンバやメソッドを含むプライベートメンバにアクセスするサンプルを掲載します。

非公開メンバをテストする場合、アクセッサを生成する方法もVisual Studio 2005 から存在します。単体テストクラスをテストメニュー→新しいテストから、テストウィザードで作成したり、テスト対象のクラスをエディタで開き、右クリック→プライベート アクセッサーの作成 で、プライベートアクセッサクラスを生成or再生成できます。プライベートアクセッサの作成方法や使用したテストは以下のリンクを参照してください。

プライベート メソッド、内部メソッド、およびフレンド メソッドの単体テスト
http://msdn.microsoft.com/ja-jp/library/bb385974(v=VS.100).aspx
プライベート アクセサの使用
http://msdn.microsoft.com/ja-jp/library/bb385974(v=VS.90).aspx

 1. テスト用にクラスの作成

クラスライブラリプロジェクトを作成し、単体テストのサンプルように(静的も含めて)プライベートなメンバ、プロパティを含むクラスを作成します。

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

namespace SampleLibrary
{
    public class PrivateAccess
    {
        /// <summary>
        /// スタティックプロパティ
        /// </summary>
        private static string NameStatic
        {
            get { return typeof(PrivateAccess).Name; }
        }
        /// <summary>
        /// プライベートプロパティ
        /// </summary>
        private string Name { get; set; }

        public PrivateAccess(string name)
        {
            this.Name = name;
        }
        /// <summary>
        /// プライベートメソッド
        /// </summary>
        /// <param name="lhs"></param>
        /// <param name="rhs"></param>
        /// <returns></returns>
        private int Add(int lhs, int rhs)
        {
            return lhs + rhs;
        }
        /// <summary>
        /// スタティックメソッド
        /// </summary>
        /// <param name="lhs"></param>
        /// <param name="rhs"></param>
        /// <returns></returns>
        private static int AddStatic(int lhs, int rhs)
        {
            return lhs + rhs;
        }
        private int Subtract(int lhs, int rhs)
        {
            return lhs - rhs;
        }
    }
}

2. 単体テストクラスの作成

作成したPrivateAccess.cs をエディタで開いて右クリック→単体テストの作成から単体テストを作成します。もしくは、テストメニューから新しいテストを選択して、単体テストウィザードを選択して単体テストウィザードを起動してPrivateAccess.csの単体テストを作成します。あとは作成された単体テストを次のように編集して、PrivateObject, PrivateType を使用して非公開メンバのテストを実施します。

using SampleLibrary;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;

namespace SampleLibraryTest
{    
    /// <summary>
    /// PrivateAccess クラスの テスト クラスです。
    ///</summary>
    [TestClass()]
    public class PrivateAccessTest
    {
        private TestContext testContextInstance;
        /// <summary>
        ///現在のテストの実行についての情報および機能を
        ///提供するテスト コンテキストを取得または設定します。
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        /// <summary>
        ///PrivateAccess コンストラクター のテスト
        ///PrivateObject を使用してプライベートなプロパティにアクセスする
        ///</summary>
        [TestMethod()]
        public void PrivateAccessConstructorTest()
        {
            // Arange
            PrivateObject po = new PrivateObject(typeof(PrivateAccess), new object[] { "こんにちわ" });
            string name = "こんにちわ";
            // Act
            // PrivateAccess target = new PrivateAccess(name);
            // Assert
            Assert.AreEqual(name, po.GetProperty("Name"));   
        }

        /// <summary>
        ///Add のテスト
        ///</summary>
        [TestMethod()]
        //[DeploymentItem("SampleLibrary.dll")]
        public void AddTest()
        {
            PrivateObject po = new PrivateObject(typeof(PrivateAccess), new object[] { "こんにちは" });
            // 自動作成されるアクセッサクラスは使用しない
            //PrivateAccess_Accessor target = new PrivateAccess_Accessor(param0); 
            int lhs = 2; 
            int rhs = 3; 
            int expected = 5; 
            int actual;
            actual = (int) po.Invoke("Add", new object[]{lhs, rhs});
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        ///AddStatic のテスト
        ///</summary>
        [TestMethod()]
        //[DeploymentItem("SampleLibrary.dll")]
        public void AddStaticTest()
        {
            PrivateType pt = new PrivateType(typeof(PrivateAccess));
            int lhs = 1; 
            int rhs = 2; 
            int expected = 3;
            int actual;
            actual = (int) pt.InvokeStatic("AddStatic", new object[] { lhs, rhs });
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        ///Name のテスト
        ///</summary>
        [TestMethod()]
        [DeploymentItem("SampleLibrary.dll")]
        public void NameTest()
        {
            PrivateObject po = new PrivateObject(new PrivateAccess("hello"));
            // 自動生成されたクラスはコメント
            // PrivateAccess_Accessor target = new PrivateAccess_Accessor(param0); // TODO: 適切な値に初期化してください
            string expected = "hello";
            string actual = po.GetProperty("Name") as string;
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        ///NameStatic のテスト
        ///</summary>
        [TestMethod()]
        //[DeploymentItem("SampleLibrary.dll")]
        public void NameStaticTest()
        {
            PrivateType pt = new PrivateType(typeof(PrivateAccess));
            string actual;
            actual = pt.GetStaticProperty("NameStatic") as string;
            Assert.AreEqual("PrivateAccess", actual);
        }
    }
}

3. まとめ

説明は以上です。単体テストは公開のメンバのみ行えばよいという考えもあると思いますが、Private, Protected など非公開メンバに対してテストを行いたい場合の方法としてPrivateObject, PrivateType を使用する方法のメモを記載しました。プライベートなメンバをテストする方法として、冒頭のリンクで紹介したプライベートメンバのアクセッサを使用する方法もあります。