首页 > 解决方案 > 在 C# 中使用 Mocks 和子类对受保护的方法进行单元测试

问题描述

我正在尝试对公共类中的一些受保护方法进行单元测试。我从这个类继承并通过从它返回超类的方法来测试继承的类。我的示例代码如下:

基类:

public class A
{
    // Private variables here
    public A(){ }

    protected virtual bool MethodOfA()
    {
        bool returnValue = false;
        //some operation here to set returnValue
        return returnValue;
    }

    protected bool AnotherMethodOfA()
    {
        bool anotherReturnValue = false;
        bool operationCheck = MethodOfA();

        if(operationCheck)
        {
            //do something to set the value of anotherReturnValue
        }

        return anotherReturnValue;
    }
}

继承类:

public class B : A
{
    // Private variables here
    public B():base() { }

    public new bool MethodOfA()
    {
        return base.MethodOfA();
    }

    public new bool AnotherMethodOfA()
    {
        var testMock = new Mock<A>();

        //This is the part where I'm not sure how to get it to work.
        testMock.CallBase = true; // I have tried it with and without this statement. couldn't get it to work
        testMock.Protected()
                .Setup<bool>("MethodOfA")
                .Returns(true);

        return base.AnotherMethodOfA();
    }
}

测试:

public class TestB
{
    private readonly B _sut

    //some Mocks here for setup

    public TestB()
    {
        _sut = new B();
    }


    [Fact]
    public void AnotherMethodOfA_Test()
    {
        var result = _sut.AnotherMethodOfA();
        Assert.True(result);
    }
}

我需要的基本上是,当我从 TestB 类运行我的测试时,它在需要调用 'MethodOfA()' 的那个方法中点击 '_sut.AnotherMethodOfA()',它应该只使用我提供的值在 Mock 中继续执行,而不是调用实际的方法(它现在正在执行)。

现在,如果我的 Method-Under-Test 更简单并且不在其中调用另一个方法,那么整个事情就会很简单(在我的许多其他方法中就是这种情况,我已经成功地测试了这些方法),但是由于这种方法在执行过程中调用另一个方法,我需要模拟这个中间方法并将其传递给我的被测方法。

标签: c#unit-testingmockingmoqaccess-modifiers

解决方案


这很好,因为你可以用一块石头杀死两只鸟:减少或消除继承,以及使用依赖注入。

与其创建protected方法,不如将方法所做的事情想象为您的类所依赖的抽象。

代替

protected bool AnotherMethodOfA()

想象

public interface IThingThatDoesSomethingAndReturnsABoolean
{
    bool MethodThatReturnsBool();
}

或者

public delegate bool FunctionThatReturnsBool();

然后,重写类A如下:

public class A
{
    private readonly IThingThatDoesSomethingAndReturnsABoolean _thing;

    public A(IThingThatDoesSomethingAndReturnsABoolean thing)
    {
        _thing = thing;
    }

    protected bool AnotherMethodOfA()
    {
        bool anotherReturnValue = false;
        bool operationCheck = _thing.MethodThatReturnsBool();

        if (operationCheck)
        {
            //do something to set the value of anotherReturnValue
        }

        return anotherReturnValue;
    }
}

如果您需要更改返回 a 的任何内容的实现,则bool不必通过继承来完成A。这是一种常见的模式,但它往往会纠缠不清并产生问题,包括您要询问的确切问题。

相反,您所要做的就是提供不同的IThingThatDoesSomethingAndReturnsABoolean.

现在一切都可以测试了。您可以A通过提供接口的模拟来进行测试。您希望返回 的方法bool是可测试的,现在是因为它不再是protected某个其他类的方法。

这被称为优先组合而不是继承。不是让类通过相互继承来协同工作,而是编写单独的类来完成不同的事情并将它们组合在一起以协同工作。

A如果您的依赖类需要它以前作为属性或字段访问的值“拥有” ,您可以将它们作为方法的参数。这使得很容易准确地查看该方法需要多少信息A

在某些情况下继承是有意义的,但是将功能分离为依赖项而不是将它们构建到继承层次结构中是一种很好的做法,这将使您的代码可测试并防止以后可能出现的其他问题。


推荐阅读