📝

MsTestのprivateメンバ操作

2020/11/03に公開

テスト対象

/// <summary>
/// テスト対象クラス
/// </summary>
public class TestTargetClass
{
    /// <summary>
    /// Privateメンバ変数
    /// </summary>
    private int _privateMember = 0;

    /// <summary>
    /// PrivateStaticメンバ変数
    /// </summary>
    private static string _privateStaticMember = "";

    /// <summary>
    /// Publicメソッド
    /// </summary>
    /// <param name="param"></param>
    /// <returns></returns>
    public int PublicMethod(int param)
    {
        return param + 1;
    }

    /// <summary>
    /// Privateメソッド
    /// </summary>
    /// <param name="val1"></param>
    /// <param name="val2"></param>
    /// <returns></returns>
    private int PrivateMethod(int val1, int val2)
    {
        return _privateMember + val1 + val2;
    }

    /// <summary>
    /// Privateメソッド(非同期)
    /// </summary>
    /// <param name="val1"></param>
    /// <param name="val2"></param>
    /// <returns></returns>
    private async Task<int> PrivateMethodAsync(int val1, int val2)
    {
        await Task.Delay(0);
        return val1 + val2;
    }

    /// <summary>
    /// PrivateStaticメソッド
    /// </summary>
    /// <returns></returns>
    private static string GetPrivateStaticMember()
    {
        return _privateStaticMember;
    }
}

テストコード

Public メソッド

通常ケース

[TestMethod()]
public void PublicMethodTest()
{
    var instance = new TestTargetClass();
    var ret = instance.PublicMethod(1);
    Assert.AreEqual(ret, 2);
}

Private メソッド+Private メンバ変数

PrivateObject の SetFieldOrProperty()でメンバ変数を変更.
または SetField(),SetProperty().
privateObject.Invoke()でメソッド呼び出し.第 2 引数以降で呼び出したいメソッドの引数を設定.

[TestMethod()]
public void PrivateMethodTest()
{
    var instance = new TestTargetClass();
    var privateObject = new PrivateObject(instance);

    // メンバ変数値変更
    privateObject.SetFieldOrProperty("_privateMember", 100);
    // Privateメソッド呼び出し
    var ret = privateObject.Invoke("PrivateMethod", 100, 200);
    Assert.AreEqual(ret, 300);
}

Private メソッド(非同期)

呼び出しを戻り値の型でくるんで await.

[TestMethod()]
public async Task PrivateMethodAsyncTest()
{
    var instance = new TestTargetClass();
    var privateObject = new PrivateObject(instance);
    // 戻り値の型でくるんでawait
    var ret = await (privateObject.Invoke("PrivateMethodAsync", 100, 200) as Task<int>);
    Assert.AreEqual(ret, 300);
}

PrivateStatic メソッド+PrivateStatic メンバ変数

PrivateType の SetStaticFieldOrProperty()と InvokeStatic()を使用.
基本は通常の Private のときと同じ.

[TestMethod()]
public void GetPrivateStaticMemberTest()
{
    var privateType = new PrivateType(typeof(TestTargetClass));
    // static変数値変更
    privateType.SetStaticFieldOrProperty("_privateStaticMember", "ヨシッ!");
    // staticメソッド呼び出し
    var ret = privateType.InvokeStatic("GetPrivateStaticMember");

    Assert.AreEqual(ret, "ヨシッ!");
}

2023/03/18追記

当時この記事書いたときはとにかくテスト書けたらいいかと思っていました。
ただ色々テストについて勉強した今、Privateメソッドのテストコードを書く前に適切にクラスの分離などをすべきだと思いました。
Privateメソッド等触るのはほんとうに最終手段かなと。

Discussion