我正在构建一个类库,它将有一些公共和私有方法。我希望能够对私有方法进行单元测试(主要是在开发过程中,但也可以用于将来的重构)。
正确的做法是什么?
我正在构建一个类库,它将有一些公共和私有方法。我希望能够对私有方法进行单元测试(主要是在开发过程中,但也可以用于将来的重构)。
正确的做法是什么?
当前回答
在c#中,您可以使用我下面提供的代码。尽管我认为只有在绝对需要的时候才应该进行私有方法的单元测试。我遇到过一些我觉得有必要这样做的案例。下面是我在UnitTestBase类中创建的一些c#方法,我继承了我的UnitTest类(你也可以把它放在一个静态的“助手”类中)。HTH
protected internal static TResult? InvokePrivateInstanceMethod<TType, TResult>(string methodName, object?[]? methodArguments = null, params object?[]? constructorArguments)
{
var classType = typeof(TType);
var instance = Activator.CreateInstance(classType, constructorArguments);
var privateMethodInfo = classType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
.FirstOrDefault(m => m.IsPrivate &&
m.Name.Equals(methodName, StringComparison.Ordinal) &&
m.ReturnType.Equals(typeof(TResult)));
if (privateMethodInfo is null)
{
throw new MissingMethodException(classType.FullName, methodName);
}
var methodResult = privateMethodInfo.Invoke(instance, methodArguments);
if (methodResult is not null)
{
return (TResult)methodResult;
}
return default;
}
protected internal static async Task<TResult?> InvokePrivateInstanceMethodAsync<TType, TResult>(string methodName, object?[]? methodArguments = null, params object?[]? constructorArguments)
{
var classType = typeof(TType);
var instance = Activator.CreateInstance(classType, constructorArguments);
var privateMethodInfo = classType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
.FirstOrDefault(m => m.IsPrivate &&
m.Name.Equals(methodName, StringComparison.Ordinal) &&
m.ReturnType.Equals(typeof(Task<TResult>)));
if (privateMethodInfo is null)
{
throw new MissingMethodException(classType.FullName, methodName);
}
var methodResult = privateMethodInfo.Invoke(instance, methodArguments);
if (methodResult is not null)
{
return await (Task<TResult>)methodResult;
}
return default;
}
其他回答
我倾向于不使用编译器指令,因为它们很快就会把事情弄得乱七八糟。如果你真的需要它们,一种缓解它的方法是把它们放在一个partial类中,并让你的构建在制作产品版本时忽略那个.cs文件。
首先,您不应该测试代码的私有方法。你应该测试“公共接口”或API,即类的公共内容。API是所有公开给外部调用者的公共方法。
原因是一旦您开始测试类的私有方法和内部结构,您就将类的实现(私有的东西)耦合到您的测试中。这意味着当您决定更改实现细节时,您也必须更改您的测试。
出于这个原因,你应该避免使用internalsvisibletoattribute。
以下是Ian Cooper关于这个主题的演讲:Ian Cooper: TDD,哪里出了问题
1)如果你有一个遗留代码,那么测试私有方法的唯一方法就是反射。
2)如果它是新代码,那么你有以下选项:
使用反射(使之复杂) 在同一个类中编写单元测试(使生产代码变得丑陋 其中还包含测试代码) 在某种util类中重构并使方法为公共 使用@VisibleForTesting注释并删除private
I prefer the annotation method, simplest and least complicated. The only issue is that we have increased the visibility which I think is not a big concern. We should always be coding to interface, so if we have an interface MyService and an implementation MyServiceImpl then we can have the corresponding test classes that is MyServiceTest (test interface methods) and MyServiceImplTest (test private methods). All clients should anyway be using the interface so in a way even though the visibility of the private method has been increased it should not really matter.
将它们声明为内部的,然后使用InternalsVisibleToAttribute允许单元测试程序集看到它们。
一种方法是保护您的方法,并编写一个继承要测试的类的测试fixture。这样,您既没有将方法公开,但是启用了测试。