19
Help us understand the problem. What are the problem?

posted at

updated at

[C#]private、internalなメソッドのUnitTest

もくじ

やりたいこと

作ったクラスやメソッドのUnitTestをしたいが、privateやinternalにしているメソッドはUnitTestプロジェクトのコードから見えないので、普通にはテストできなかった。どうにかしてテストしたい。

やり方

PrivateObjectやPrivateTypeクラスを使う。

private、internalメソッドを外から呼ぶ

PrivateObjectクラスのInvokeメソッドを使う。

  • テスト対象クラスをnewする
  • それを引数にして、PrivateObjectクラスをnewする
  • そのインスタンスで、PrivateObjectのInvokeメソッドを呼ぶ
  • Invokeの第一引数には対象のprivateのメソッド名を渡す
  • Invokeの第二引数以降にはそのメソッドに渡す引数を渡す
var cls1 = new Class1();
var pbObj = new PrivateObject(cls1);
ret = (string)pbObj.Invoke("AddPrivate", 1, 2);

private static、internal staticメソッドを外から呼ぶ

PrivateTypeクラスのInvokeStaticメソッドを使う。

  • テスト対象のクラスのtypeを引数にして、PrivateTypeをnewする
  • そのインスタンスで、PrivateTypeのInvokeStaticメソッドを呼ぶ
  • InvokeStaticの第一引数には対象のprivateのメソッド名を渡す
  • InvokeStaticの第二引数以降にはそのメソッドに渡す引数を渡す
PrivateType privateType = new PrivateType(typeof(Class1));
ret = (string)privateType.InvokeStatic("AddPrivateStatic", 1, 2);

子クラスから見た親クラスのprivateメソッドを外から呼ぶ

PrivateObjectクラスのInvokeメソッドを使う。
その際、PrivateObjectをnewするときの引数を変える。(詳細はサンプル参照)

  • テスト対象クラス(子クラス)をnewする
  • それを第一引数、第二引数に親クラスのtypeを引数にしたPrivateTypeをnewしたものを渡して、PrivateObjectクラスをnewする
  • そのインスタンスで、PrivateObjectのInvokeメソッドを呼ぶ
var clssub = new Class1Sub();
var pbObjSub = new PrivateObject(clssub, new PrivateType(typeof(Class1)));
ret = (string)pbObjSub.Invoke("AddPrivate", 1, 2);

サンプルコード

テストしたいdllを呼んで、UnitTestを行うためのプロジェクトのコード。

UnitTest1.cs
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ClassLibrary1;

namespace UnitTestProject1
{
    // 参考:https://www.gesource.jp/weblog/?p=7742

    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            var ret = "";
            var cls1 = new Class1();

            // publicなメソッドのテスト
            ret = cls1.AddPublic(1, 2);
            ret = Class1.AddPublicStatic(1, 2);

            // private、internalなメソッドのテスト
            var pbObj = new PrivateObject(cls1);
            ret = (string)pbObj.Invoke("AddPrivate", 1, 2);
            ret = (string)pbObj.Invoke("AddInternal", 1, 2);

            // private static、internal staticなメソッドのテスト
            PrivateType privateType = new PrivateType(typeof(Class1));
            ret = (string)privateType.InvokeStatic("AddPrivateStatic", 1, 2);
            ret = (string)privateType.InvokeStatic("AddInternalStatic", 1, 2);

            // 子クラスから見た親クラスのprivateメソッドをテスト
            var clssub = new Class1Sub();
            var pbObjSub = new PrivateObject(clssub, new PrivateType(typeof(Class1)));
            ret = (string)pbObjSub.Invoke("AddPrivate", 1, 2);
            ret = (string)pbObjSub.Invoke("AddInternal", 1, 2);

            // 子クラスから見た親クラスのprivate staticメソッドは、
            // 親クラスのprivate staticテストと同じ。
        }
    }
}

UnitTestから呼ぶ実験用dllのコード。
public、private、internalなメソッドと、その3つをstaticにしたメソッドを含む。

Class1.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;

namespace ClassLibrary1
{
    public class Class1
    {
        public string AddPublic(int val1, int val2)
        {
            return MethodBase.GetCurrentMethod().Name + " " + (val1 + val2).ToString();
        }

        internal string AddInternal(int val1, int val2)
        {
            return MethodBase.GetCurrentMethod().Name + " " + (val1 + val2).ToString();
        }

        private string AddPrivate(int val1, int val2)
        {
            return MethodBase.GetCurrentMethod().Name + " " + (val1 + val2).ToString();
        }

        public static string AddPublicStatic(int val1, int val2)
        {
            return MethodBase.GetCurrentMethod().Name + " " + (val1 + val2).ToString();
        }

        internal static string AddInternalStatic(int val1, int val2)
        {
            return MethodBase.GetCurrentMethod().Name + " " + (val1 + val2).ToString();
        }

        private static string AddPrivateStatic(int val1, int val2)
        {
            return MethodBase.GetCurrentMethod().Name + " " + (val1 + val2).ToString();
        }
    }

    public class Class1Sub : Class1
    {

    }
}

コード

参考

MSTestでprivateメソッドをテストする
ほぼこちらを参考にさせていただいています。ありがとうございます。
https://www.gesource.jp/weblog/?p=7742

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
19
Help us understand the problem. What are the problem?