Модульное тестирование защищенных методов с использованием Mocks и подклассов в C # - PullRequest
0 голосов
/ 06 декабря 2018

Я пытаюсь выполнить модульное тестирование нескольких защищенных методов, находящихся в открытом классе.Я наследую от этого класса и тестирую унаследованный класс, возвращая из него методы суперкласса.Мой пример кода выглядит следующим образом:

Базовый класс:

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 (), он должен просто использовать значение, которое я предоставляю вСмоделируйте и переходите к выполнению вместо вызова фактического метода (который он сейчас делает).

Теперь, если бы мой тестируемый метод был проще и не вызывал другой метод внутри него, вся эта вещь была быбыли простыми (что имеет место во многих других моих методах, и я уже успешно их тестирую), но так как этот метод вызывает другой метод во время его выполнения, мне нужно смоделировать этот промежуточный метод и передать его моему Методу подтест.

1 Ответ

0 голосов
/ 06 декабря 2018

Это хорошо, потому что вы можете убить двух зайцев одним выстрелом: уменьшить или исключить наследование и использовать инъекцию зависимостей.

Вместо создания метода 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.

. Существуют случаи, когда наследование имеет смысл, но рекомендуется разделять функциональные возможности на зависимости, а не встраивать их в иерархию наследования.сделает ваш код тестируемым и предотвратит другие головные боли, которые могут возникнуть позже.

...