Visual Studio允许通过自动生成的访问器类对私有方法进行单元测试。我已经编写了一个私有方法的测试,它编译成功,但在运行时失败。一个相当小的版本的代码和测试是:
//in project MyProj
class TypeA
{
private List<TypeB> myList = new List<TypeB>();
private class TypeB
{
public TypeB()
{
}
}
public TypeA()
{
}
private void MyFunc()
{
//processing of myList that changes state of instance
}
}
//in project TestMyProj
public void MyFuncTest()
{
TypeA_Accessor target = new TypeA_Accessor();
//following line is the one that throws exception
target.myList.Add(new TypeA_Accessor.TypeB());
target.MyFunc();
//check changed state of target
}
运行时错误为:
Object of type System.Collections.Generic.List`1[MyProj.TypeA.TypeA_Accessor+TypeB]' cannot be converted to type 'System.Collections.Generic.List`1[MyProj.TypeA.TypeA+TypeB]'.
根据智能感知-因此我猜编译器-目标类型是TypeA_Accessor。但是在运行时它的类型是TypeA,因此列表添加失败。
有什么方法可以停止这个错误吗?或者,更有可能的是,其他人有什么其他的建议(我预测可能是“不要测试私有方法”和“不要使用单元测试来操纵对象的状态”)。
public static class PrivateMethodTester
{
public static object InvokePrivateMethodWithReturnType<T>(this T testObject, string methodName, Type[] methodParamTypes, object[] parameters)
{
//shows that we want the nonpublic, static, or instance methods.
var flags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Instance;
//gets the method, but we need the methodparamtypes so that we don't accidentally get an ambiguous method with different params.
MethodInfo methodInfo = testObject.GetType().GetMethod(methodName, flags, null, methodParamTypes, null);
if (methodInfo == null)
{
throw new Exception("Unable to find method.");
}
//invokes our method on our object with the parameters.
var result = methodInfo.Invoke(testObject, parameters);
if (result is Task task)
{
//if it is a task, it won't resolve without forcing it to resolve, which means we won't get our exceptions.
task.GetAwaiter().GetResult();
}
return result;
}
}
这样称呼它:
Type[] paramTypes = new Type[] { typeof(OrderTender), typeof(string) };
var parameters = new object[] { orderTender, OrderErrorReasonNames.FailedToCloneTransaction };
myClass.InvokePrivateMethodWithReturnType("myPrivateMethodName", paramTypes, parameters);
您可以使用嵌套类来测试私有方法。例如(使用NUnit v3):
internal static class A
{
// ... other code
private static Int32 Sum(Int32 a, Int32 b) => a + b;
[TestFixture]
private static class UnitTests
{
[Test]
public static void OnePlusTwoEqualsThree()
{
Assert.AreEqual(3, Sum(1, 2));
}
}
}
此外,可以使用“部分类”特性将测试相关代码移动到另一个文件中,使用“条件编译”将其排除在发布版本之外,等等。先进的例子:
文件交流
internal static partial class A
{
// ... other code
private static Int32 Sum(Int32 a, Int32 b) => a + b;
}
文件A.UnitTests.cs
#if UNIT_TESTING
partial class A
{
[TestFixture]
private static class UnitTests
{
[Test]
public static void OnePlusTwoEqualsThree()
{
Assert.AreEqual(3, Sum(1, 2));
}
}
}
#endif