xUnitでクラスのプライベート メソッドのテストをするコードを紹介します。
xUnitテストプロジェクトでテストを実施する場合、テストされるクラスのあるプロジェクトをxUnitテストプロジェクトに参照で追加し、
クラスのインスタンスをテストプロジェクトで作成するか、静的クラスのメソッドをテストプロジェクトから呼び出す必要があります。
しかし、クラスのメソッドがプライベートメソッドで定義されている場合、クラスのメソッドをテストプロジェクトから呼び出すことができません。
この記事ではクラスのプライベートメソッドをxUnitテストプロジェクトでテストするコードを紹介します。
以下のコードを記述します。
using System;
namespace xUnitPrivateMethod
{
public class MyClass
{
public int Func(int a, int b, int c)
{
int sa = subProc1(a, c);
int sb = subProc2(b, c);
int sc = subProc3(a, b);
return (sa + sb) * sc;
}
private int subProc1(int a, int b)
{
return a + b;
}
private int subProc2(int a, int b)
{
return a * b;
}
private int subProc3(int a, int b)
{
return a - b;
}
}
}
using System;
using Xunit;
using xUnitPrivateMethod;
namespace xUnitPrivateMethodTest
{
public class UnitTestMain
{
[Fact]
public void Test1()
{
MyClass mc = new MyClass();
Assert.Equal(5, mc.Func(1, 2, 3));
}
}
}
テストプロジェクトの UnitTestMain.cs から、MyClassのインスタンスを作成し、MyClassのFunc
メソッドのテストを実行します。
テストは失敗しますが、テストは実行できます。
上記の状態から、テストプロジェクトのテストクラスに新しいテストメソッドを作成し、以下のコードを記述します。
MyClassのインスタンスを作成し、プライベートメソッドを呼び出すコードを記述します。
using System;
using Xunit;
using xUnitPrivateMethod;
namespace xUnitPrivateMethodTest
{
public class UnitTestMain
{
[Fact]
public void Test1()
{
MyClass mc = new MyClass();
Assert.Equal(5, mc.Func(1, 2, 3));
}
[Fact]
public void Test2()
{
MyClass mc = new MyClass();
Assert.Equal(5, mc.subProc1(1, 2, 3));
}
}
}
クラス外からプライベートメソッドを呼び出すことはできないため、コンパイルエラーとなり、次のエラーメッセージが表示されます。
クラスのプライベートメソッドをテストせずに、Publicメソッドをテストするよう、設計を変更します。
オブジェクト、関数に対するinとout をテストすることがテストの基本のため、内部のプライベートメソッドのテストは冗長である可能性があります。
通常の呼び出しでは、プライベートのメソッドは呼び出せないため、リフレクションを利用してクラスのプライベートメソッドにアクセスします。
リフレクションの動作の詳細についてはこちらの記事を参照してください。
using System;
using Xunit;
using xUnitPrivateMethod;
using System.Reflection;
namespace xUnitPrivateMethodTest
{
public class UnitTestMain
{
[Fact]
public void Test1()
{
MyClass mc = new MyClass();
Assert.Equal(5, mc.Func(1, 2, 3));
}
[Fact]
public void Test2()
{
var obj = Activator.CreateInstance(typeof(MyClass));
MethodInfo method = typeof(MyClass).GetMethod("subProc1", BindingFlags.NonPublic | BindingFlags.Instance);
int result = (int)method.Invoke(obj, new object[] { 1, 2});
Assert.Equal(3, result);
}
}
}
または、下記コードの Test3()
メソッドの記述方式でも同様の処理が実装できます。
using System;
using Xunit;
using xUnitPrivateMethod;
using System.Reflection;
namespace xUnitPrivateMethodTest
{
public class UnitTestMain
{
[Fact]
public void Test1()
{
MyClass mc = new MyClass();
Assert.Equal(5, mc.Func(1, 2, 3));
}
[Fact]
public void Test3()
{
Type t = typeof(MyClass);
//インスタンス作成
object obj = t.InvokeMember("MyClass", System.Reflection.BindingFlags.CreateInstance, null, null, null);
//メソッド呼び出し作成
int result = (int)t.InvokeMember("subProc1", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, obj, new object[] { 1, 2 });
Assert.Equal(3, result);
}
}
}
リフレクションでのメソッド呼び出しで InvokeAttr のパラメーターに BindingFlags.NonPublic
BindingFlags.Instance
を指定すると、
クラスのプライベートメソッドの呼び出しやメソッドオブジェクトの取得ができます。
この機能を利用してテストプロジェクトから、プライベートメソッドの呼び出しができます。