使用C#中的Mocks和子类对受保护的方法进行单元测试

时间:2018-12-05 22:27:46

标签: c# unit-testing mocking moq access-modifiers

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

基类:

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类运行测试时,它命中了'_sut.AnotherMethodOfA()',在该方法中需要调用'MethodOfA()'的地方,它应该只使用该值我在Mock中提供的代码并继续执行,而不是调用实际方法(现在正在执行)。

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

1 个答案:

答案 0 :(得分:0)

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

不要创建一个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;
    }
}

如果您需要更改返回bool的实现,则不必通过继承A来实现。这是一种常见的模式,但是它很容易纠结并产生问题,包括您所要询问的确切问题。

相反,您要做的就是提供IThingThatDoesSomethingAndReturnsABoolean的不同实现。

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

这称为优先继承而不是继承。您不必编写类来彼此继承,而要编写单​​独的类来完成不同的工作,并将它们组合在一起工作。

如果依赖类需要A“拥有”的值,该值以前作为属性或字段访问,则可以将其作为方法的参数。这样可以很容易地从A准确地了解该方法需要多少信息。

在某些情况下,继承是有意义的,但是将功能划分为依赖关系而不是将其构建为继承层次结构是一种好习惯,它将使您的代码可测试,并避免以后可能出现的其他麻烦。