使用Moq在C#中进行单元测试保护的方法

mag*_*tte 15 c# moq

最近我注意到你可以使用Moq对抽象基类进行单元测试,而不是在测试中创建一个实现抽象基类的虚拟类.请参见如何使用moq测试抽象类中的具体方法?你可以这样做:

public abstract class MyAbstractClass 
{
    public virtual void MyMethod()
    {
        // ...    
    }
}

[Test]
public void MyMethodTest()
{
    // Arrange            
    Mock<MyAbstractClass> mock = new Mock<MyAbstractClass>() { CallBase = true };

    // Act
    mock.Object.MyMethod();

    // Assert
    // ... 
}
Run Code Online (Sandbox Code Playgroud)

现在我想知道是否有类似的技术允许我测试受保护的成员而不必创建包装类.即你如何测试这种方法:

public class MyClassWithProtectedMethod
{
    protected void MyProtectedMethod()
    {

    }
}
Run Code Online (Sandbox Code Playgroud)

我知道Moq.Protected命名空间,但据我所知它只允许你设置期望与例如

mock.Protected().Setup("MyProtectedMethod").Verifiable();
Run Code Online (Sandbox Code Playgroud)

我也知道这里显而易见的答案是"不测试受保护的方法,只测试公共方法",然而这是另一个争论!我只想知道是否可以使用Moq.

更新:下面是我正常测试的方法:

public class MyClassWithProtectedMethodTester : MyClassWithProtectedMethod
{
    public void MyProtectedMethod()
    {
        base.MyProtectedMethod();
    }
}
Run Code Online (Sandbox Code Playgroud)

提前致谢.

Yur*_*kyy 9

Moq调用受保护成员的另一种方法是以下模板:

  1. 在您的班级中,受保护的成员将您的功能标记为虚拟.例如:

    public class ClassProtected
        {
            public string CallingFunction(Customer customer)
            {
                var firstName = ProtectedFunction(customer.FirstName);
                var lastName = ProtectedFunction(customer.LastName);
    
                return string.Format("{0}, {1}", lastName, firstName);
            }
    
            protected virtual string ProtectedFunction(string value)
            {
                return value.Replace("SAP", string.Empty);
            }
        }
    
    Run Code Online (Sandbox Code Playgroud)

然后在您的单元测试中添加引用

 using Moq.Protected;
Run Code Online (Sandbox Code Playgroud)

在您的单元测试中,您可以编写以下内容:

    [TestFixture]
    public class TestClassProttected
    {
        [Test]
        public void all_bad_words_should_be_scrubbed()
        {
            //Arrange
            var mockCustomerNameFormatter = new Mock<ClassProtected>();

            mockCustomerNameFormatter.Protected()
                .Setup<string>("ProtectedFunction", ItExpr.IsAny<string>())
                .Returns("here can be any value")
                .Verifiable(); // you should call this function in any case. Without calling next Verify will not give you any benefit at all

            //Act
            mockCustomerNameFormatter.Object.CallingFunction(new Customer());

            //Assert
            mockCustomerNameFormatter.Verify();
        }
    }
Run Code Online (Sandbox Code Playgroud)

记下ItExpr.它应该用来代替它.另一个问题在Verifiable等着你.我不知道为什么,但是没有调用Verifiable Verify就不会被调用.


Tru*_*ill 8

对于初学者来说,单元测试抽象方法毫无意义.没有实施!您可能希望对不纯的抽象类进行单元测试,验证是否调用了抽象方法:

[Test]
public void Do_WhenCalled_CallsMyAbstractMethod()
{
    var sutMock = new Mock<MyAbstractClass>() { CallBase = true };
    sutMock.Object.Do();
    sutMock.Verify(x => x.MyAbstractMethod());
}

public abstract class MyAbstractClass
{
    public void Do()
    {
        MyAbstractMethod();
    }

    public abstract void MyAbstractMethod();
}
Run Code Online (Sandbox Code Playgroud)

请注意,我设置Call​​Base将其转换为部分模拟,以防Do是虚拟的.否则Moq将取代Do方法的实现.

使用Protected(),您可以验证以类似方式调用受保护的方法.

当您使用Moq或其他库创建模拟时,重点是覆盖实现.测试受保护的方法涉及公开现有实现.这不是Moq的目的.Protected()只是让你访问(大概是通过反射,因为它是基于字符串的)来覆盖受保护的成员.

使用调用受保护方法的方法编写测试后代类,或者在单元测试中使用反射来调用受保护方法.

或者,更好的是,不要直接测试受保护的方法.


Ant*_*ram 5

您已经触及了"测试公共API,而不是私有"思想过程,并且您已经提到了从类继承然后以这种方式测试其受保护成员的技术.这两种都是有效的方法.

在这一切之下,简单的事实是,您认为这个实现细节(因为这是私有或受保护的成员)非常重要,可以直接测试而不是通过使用它的公共API间接测试.如果这重要,也许重要的是推广到自己的班级.(毕竟,如果它是如此重要,也许它是一个MyAbstractClass不应该有的责任.)类的实例将在内部受到保护MyAbstractClass,因此只有基类和派生类型才能访问实例,但类本身将是完全可测试,否则可在其他地方使用,如果需要的话.

abstract class MyAbstractClass 
{
     protected ImportantMethodDoer doer;
}

class ImportantMethodDoer
{
     public void Do() { }
}
Run Code Online (Sandbox Code Playgroud)

否则,您将离开*已经确定的方法.


*Moq可能会或可能不会提供一些获取私人或受保护成员的机制,我不能说,因为我不使用该特定工具.我的答案更多来自建筑的立场.