单元测试 C # 保护方法

我来自 JavaEE 世界,但是现在我正在开发一个。网络项目。在 Java 中,当我想要测试一个受保护的方法时,非常容易,只要测试类具有相同的包名就足够了。

C # 有类似的东西吗?单元测试受保护的方法是否有良好的实践?我只发现框架和人们说我应该只测试公共方法。

不需要任何框架就可以做到。

70031 次浏览

You can inherit the class you are testing on your test class.

[TestClass]
public class Test1 : SomeClass
{
[TestMethod]
public void MyTest
{
Assert.AreEqual(1, ProtectedMethod());
}


}

You can use reflection to invoke private and protected methods.

See here for more:

http://msdn.microsoft.com/en-us/library/66btctbe.aspx

You can use PrivateObject class to access all the private/ protected methods/ fields.

PrivateObject is a class in the Microsoft unit testing framework which is a wrapper that enables calling normally inaccessible members for unit testing.

You can expose the protected methods in a new class that inherits the class you want to test.

public class ExposedClassToTest : ClassToTest
{
public bool ExposedProtectedMethod(int parameter)
{
return base.ProtectedMethod(parameter);
}
}

Another option is to use internal for these methods and then use InternalsVisibleTo to allow your test assembly to access these methods. This does not stop the methods being consumed by other classes in the same assembly, but it does stop them being accessed by other assembles that are not your test assembly.

This does not give you as much encapsulation and protection but it's pretty straight forward and can be useful.

Add to AssemblyInfo.cs in the assembly containing the internal methods

[assembly: InternalsVisibleTo("TestsAssembly")]

Although the accepted answer is the best one, it didn't solve my problem. Deriving from the protected class polluted my test class with a lot of other stuff. In the end I chose to extract the to-be-tested-logic into a public class and test that. Surely enough this will not work for everyone and might require quite some refactoring, but if you scrolled all the way up to this answer, it might just help you out. :) Here's an example

Old situation:

protected class ProtectedClass{
protected void ProtectedMethod(){
//logic you wanted to test but can't :(
}
}

New situation:

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();
}
}

You can create a stub with a public method that calls the protected method from the base class. This is also how you would use this protected method in production.

public class FooStub : Bar
{
public string MyMethodFoo()
{
return MyMethodBar();
}
}


public abstract class Bar
{
protected string MyMethodBar()
{
return "Hello World!"
}
}

Here is the extension method (what is the extension method?) I am using in my testing project. The only downside is that you have to literally write the name as a string because nameof() follows the protection rules and does not allow you to refer to protected or private members.

        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.");
}
}
}

More on this method here: https://learn.microsoft.com/en-us/dotnet/api/system.type.getmember?view=net-5.0

PS: Use methodInfo.Invoke(instance, params) to call it.