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

# もくじ
https://tera1707.com/entry/2022/02/06/144447#UnitTest


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

# やり方
PrivateObjectやPrivateTypeクラスを使う。

### private、internalメソッドを外から呼ぶ
PrivateObjectクラスのInvokeメソッドを使う。

- テスト対象クラスをnewする
- それを引数にして、PrivateObjectクラスをnewする
- そのインスタンスで、PrivateObjectのInvokeメソッドを呼ぶ
- Invokeの第一引数には対象のprivateのメソッド名を渡す
- Invokeの第二引数以降にはそのメソッドに渡す引数を渡す


```cs
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の第二引数以降にはそのメソッドに渡す引数を渡す

```cs
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メソッドを呼ぶ

```cs
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
    {

    }
}

```

# コード
https://github.com/tera1707/WPF-/tree/master/028_PrivateClassUnitTest

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