我来自Java EE世界,但现在我正在从事.Net项目。在Java中,当我想要测试一个受保护的方法时,很容易,只需要拥有与测试类相同的包名即可。
对于C#是否有类似的东西?有没有关于单元测试受保护方法的良好实践?我只发现一些框架和人们说我应该只测试公共方法。
应该可以在不使用任何框架的情况下完成...
我来自Java EE世界,但现在我正在从事.Net项目。在Java中,当我想要测试一个受保护的方法时,很容易,只需要拥有与测试类相同的包名即可。
对于C#是否有类似的东西?有没有关于单元测试受保护方法的良好实践?我只发现一些框架和人们说我应该只测试公共方法。
应该可以在不使用任何框架的情况下完成...
你可以在测试类中继承你要测试的类。
[TestClass]
public class Test1 : SomeClass
{
[TestMethod]
public void MyTest
{
Assert.AreEqual(1, ProtectedMethod());
}
}
你可以在继承你想要测试的类的新类中暴露受保护的方法。
public class ExposedClassToTest : ClassToTest
{
public bool ExposedProtectedMethod(int parameter)
{
return base.ProtectedMethod(parameter);
}
}
另一个选择是在这些方法中使用 internal
,然后使用 InternalsVisibleTo
来允许您的测试组件访问这些方法。这不会阻止其他同一程序集中的类使用这些方法,但确实阻止它们被其他非测试程序集访问。
这样做并不能提供太多封装和保护,但它相当简单直接,可以很有用。
在包含内部方法的程序集的 AssemblyInfo.cs
中添加以下内容:
[assembly: InternalsVisibleTo("TestsAssembly")]
protected
方法可以在子类中被看到(并且可以被重写),即使这些子类不在同一个程序集中,而internal
方法则不能。它们在功能上不是等效的,因此只适用于不需要真正的protected
方法的情况。 - E-Rizprotected 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();
}
}
你可以创建一个存根(stub),其中包含一个公共方法,该方法调用基类中的受保护方法。这也是在生产中使用此受保护方法的方式。
public class FooStub : Bar
{
public string MyMethodFoo()
{
return MyMethodBar();
}
}
public abstract class Bar
{
protected string MyMethodBar()
{
return "Hello World!"
}
}
这里是我在测试项目中使用的扩展方法(什么是扩展方法?)。
唯一的缺点是,您必须将名称文字字面写出来,因为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://learn.microsoft.com/en-us/dotnet/api/system.type.getmember?view=net-5.0
PS:使用methodInfo.Invoke(instance, params)
来调用它。
[TestClass]
public class Test1 : SomeClass
{
public Test1() : base()
{
}
[TestMethod]
public void MyTest
{
Assert.AreEqual(1, ProtectedMethod());
}
}
Asert.AreEqual(1, Test1.ProtectedMethod());
,因为派生类型需要在内部调用。尽管从基类继承,但基本方法仍然是受保护的。 - edwardrbaker