【发布时间】:2012-11-05 03:45:45
【问题描述】:
我来自 Java EE 世界,但现在我正在从事一个 .Net 项目。在 Java 中,当我想测试一个受保护的方法时,这很容易,只需让测试类具有相同的包名就足够了。
C# 有类似的东西吗?对受保护的方法进行单元测试有什么好的做法吗?我只发现框架和人们说我应该只测试公共方法。
应该可以在没有任何框架的情况下做到这一点……
【问题讨论】:
标签: c# .net unit-testing protected
我来自 Java EE 世界,但现在我正在从事一个 .Net 项目。在 Java 中,当我想测试一个受保护的方法时,这很容易,只需让测试类具有相同的包名就足够了。
C# 有类似的东西吗?对受保护的方法进行单元测试有什么好的做法吗?我只发现框架和人们说我应该只测试公共方法。
应该可以在没有任何框架的情况下做到这一点……
【问题讨论】:
标签: c# .net unit-testing protected
你可以在你的测试类上继承你正在测试的类。
[TestClass]
public class Test1 : SomeClass
{
[TestMethod]
public void MyTest
{
Assert.AreEqual(1, ProtectedMethod());
}
}
【讨论】:
TestClass does not contain a constructor that takes 0 arguments 错误
Asert.AreEqual(1, Test1.ProtectedMethod());,因为需要在内部调用派生类型。尽管继承自,基方法仍然受到保护。
另一种选择是对这些方法使用internal,然后使用InternalsVisibleTo 允许您的测试程序集访问这些方法。这不会阻止同一程序集中的其他类使用方法,但会阻止不是您的测试程序集的其他程序集访问它们。
这并没有为您提供尽可能多的封装和保护,但它非常简单并且很有用。
在包含内部方法的程序集中添加到AssemblyInfo.cs
[assembly: InternalsVisibleTo("TestsAssembly")]
【讨论】:
protected 方法对子类(包括程序集外的子类)可见(并可被覆盖),而 internal 则不可见。它们在功能上并不等效,因此此选项仅适用于您不需要真正的 protected 方法的情况。
您可以在继承您要测试的类的新类中公开受保护的方法。
public class ExposedClassToTest : ClassToTest
{
public bool ExposedProtectedMethod(int parameter)
{
return base.ProtectedMethod(parameter);
}
}
【讨论】:
您可以使用 PrivateObject 类来访问所有私有/受保护的方法/字段。
PrivateObject 是 Microsoft 单元测试框架中的一个类,它是一个包装器,可以调用通常无法访问的成员进行单元测试。
【讨论】:
【讨论】:
虽然公认的答案是最好的,但它并没有解决我的问题。从受保护的类派生出来的许多其他东西污染了我的测试类。最后,我选择将待测试逻辑提取到公共类中并对其进行测试。当然,这对每个人都不起作用,可能需要进行相当多的重构,但是如果您一直滚动到这个答案,它可能会对您有所帮助。 :) 这是一个例子
旧情况:
protected class ProtectedClass{
protected void ProtectedMethod(){
//logic you wanted to test but can't :(
}
}
新情况:
protected class ProtectedClass{
private INewPublicClass _newPublicClass;
public ProtectedClass(INewPublicClass newPublicClass) {
_newPublicClass = newPublicClass;
}
protected void ProtectedMethod(){
//the logic you wanted to test has been moved to another class
_newPublicClass.DoStuff();
}
}
public class NewPublicClass : INewPublicClass
{
public void DoStuff() {
//this logic can be tested!
}
}
public class NewPublicClassTest
{
NewPublicClass _target;
public void DoStuff_WithoutInput_ShouldSucceed() {
//Arrange test and call the method with the logic you want to test
_target.DoStuff();
}
}
【讨论】:
您可以使用从基类调用受保护方法的公共方法创建存根。这也是您在生产中使用这种受保护方法的方式。
public class FooStub : Bar
{
public string MyMethodFoo()
{
return MyMethodBar();
}
}
public abstract class Bar
{
protected string MyMethodBar()
{
return "Hello World!"
}
}
【讨论】:
这是我在测试项目中使用的扩展方法(what is the extension method?)。
唯一的缺点是您必须将名称写成string,因为nameof() 遵循保护规则,并且不允许您引用受保护或私有成员。
public static MethodInfo GetNonPublicMethod(this Type type, string method)
{
MemberInfo[] temp = type.GetMember(method, MemberTypes.Method, BindingFlags.NonPublic | BindingFlags.Instance);
if (temp.Length == 1)
{
if (temp[0] is MethodInfo ret)
{
return ret;
}
else
{
throw new ArgumentException("Not a method.");
}
}
else
{
if (temp.Length == 0)
{
throw new ArgumentException("Method was not found.");
}
else
{
throw new ArgumentException("Multiple methods found.");
}
}
}
更多关于此方法的信息:https://docs.microsoft.com/en-us/dotnet/api/system.type.getmember?view=net-5.0
PS:使用methodInfo.Invoke(instance, params)调用它。
【讨论】: