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

5

我需要为C#中抽象类中的内部或私有方法编写单元测试。

请考虑以下内容:

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.

如何解决这个问题?


2
为什么需要测试抽象类?因为它是无法实例化的。你不能直接在代码中使用它。应该测试你的代码所使用的派生类。 - Flydog57
1
如果您正在使用 Moq,则应该传递模拟实例而不是模拟包装器... 因此:PrivateObject privateObject = new PrivateObject(testProcessor.Object , privateType); 可能是您想要的。 - lidqy
顺便说一下:使用抽象类的模拟实例并不能测试该类。 - lidqy
3个回答

4

不应该测试私有方法:

我不会对私有方法进行单元测试。私有方法是实现细节,应该对类的用户隐藏起来。测试私有方法会破坏封装性。

更多详情请参见此帖子

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

您可以通过在测试类内创建一个私有测试类,并在测试中使用此私有类来轻松地实现这一点。

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我们不需要这样做。Moq会处理一切。更多讨论请见下文(@lidqy) - Joy

2
假设您正在使用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");
        }
    }

顺便说一下,我不知道一个抽象类的模拟会执行抽象类的实现。我以为它会创建返回默认值的存根,就像接口的模拟一样。有趣的特性...

1
谢谢lidqy!那就是我的解决方案。是的,使用Moq我们不需要从抽象类创建派生类,它会处理一切。我只是不知道我需要在这里使用Object。我尝试了typeof(MyAbstractClass),但那不是正确的位置。在更改privateObject之后,问题得到了解决!干得好! - Joy

0

你需要指定BindingFlagsNonPublic才能找到私有方法。

我不确定PrivateObject是什么,但这应该可以工作(你可以稍微修改一下以适应你的需求):

Type type = typeof(MyAbstractClass);

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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接