如何在 c# 的抽象 class 中对内部方法或私有方法进行单元测试?

How to unit test an internal method or private method in an abstract class in c#?

我必须在 c# 中为抽象 class 中的内部或私有方法编写单元测试。 考虑一下:

Public Abstract Class MyAbstractClass
{
 Private String MyMethod()
 {
   //Do Something
   return "HI";
 }
}

还有,我的测试方法:

public void MyTestMethod()
{
    var testProcessor = new Mock<MyAbstractClass>
    {
        CallBase = true
    };
    var privateType = new PrivateType(typeof(MyAbstractClass));
    PrivateObject privateObject = new PrivateObject(testProcessor , privateType);
    var resValue = (String)privateObject.Invoke("MyMethod");
    Assert.AreEqual(resValue ,"HI");
}

但是当我 运行 测试方法时,出现以下错误:

 System.Reflection.TargetException: Object does not match target type.

如何解决?

您需要将 BindingFlags 指定为 NonPublic 才能找到私有方法。

我不确定 PrivateObject 是什么,但这应该有效(您可以稍微更改它以使其适合您的需要):

Type type = typeof(MyAbstractClass);

MethodInfo method = type.GetMethod(
    methodName, BindingFlags.NonPublic | BindingFlags.Instance);

string result = (string)method.Invoke("MyMethod", null);

您不应该测试私有方法:

I do not unit test private methods. A private method is an implementation detail that should be hidden to the users of the class. Testing private methods breaks encapsulation.

有关详细信息,请参阅 this post

正如 Flydog57 所说,您应该测试派生的 class(而不是抽象的)。

您可以通过在测试 class 中创建一个私人测试 class 并在您的测试中使用这个私人 class 来轻松做到这一点。

private class MyConcreteTestClass : MyAbstractClass 
{
     public string SomeMethodThatCallsMethodInAbstractClass()
     {
          ....
     }
}

public void MyTestMethod()
{
    // Arrange
    var testProcessor = new MyConcreteTestClass();

    // Act
    var result = testProcessor.SomeMethodThatCallsMethodInAbstractClass();

    // Asset
    Assert.AreEqual(result ,"HI");
}

假设您使用 Moq 作为模拟工具,异常显示传递 testProcessor 而不是 testProcessor.Object 的结果。如果将该行更改为...

PrivateObject privateObject = new PrivateObject(testProcessor.Object, privateType);

...您将摆脱此错误。
这是它应该如何工作(在 NET Framework 中,PrivateObject 等尚未移植到 NET Core MSTest):

//productive code
    public abstract class MyAbstractClass
    {
        private string MyMethod()
        {
            //Do Something
            return "HI";
        }
    }

    //testproject

    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            var testProcessor         = new Moq.Mock<MyAbstractClass>();
            var privateType           = new PrivateType(typeof(MyAbstractClass));
            var privateObject         = new PrivateObject(testProcessor.Object, privateType);
            var resValue              = (string)privateObject.Invoke("MyMethod");
            Assert.AreEqual(resValue, "HI");
        }
    }

顺便说一下,我不知道抽象 class 的模拟会执行抽象 class 的实现。我认为它会创建 return 默认的存根,就像接口的模拟一样。
有趣的功能...