Как использовать moq для тестирования кода, вызывающего защищенных помощников

#c# #unit-testing #moq

#c# #модульное тестирование #moq

Вопрос:

В настоящее время я запускаю тесты, которые выглядят следующим образом:

 // In Blah.cs
public class ClassUnderTest

{

    public bool MethodUnderTest()

    {

        // Do a bunch of stuff...

        return HelperMethod();

    }



    protected virtual bool HelperMethod()

    {

        bool success = false;

        // Proprietary Hardware Access.

        // Database Calls.

        // File System Modifications.

        return success;

    }

}


// In TestBlah.cs

public class TestStub : ClassUnderTest

{

    public bool HelperMethodReturnValue;



    protected override bool HelperMethod()

    {

        return HelperMethodReturnValue;

    }

}



[TestClass]

public class TestingClass

{

    [TestMethod]

    public void ClassUnderTest_MethodUnderTest_TestHelperReturnsTrue()

    {

        var stub = new TestStub();

        stub.HelperMethodReturnValue = true;

        Assert.IsTrue(stub.MethodUnderTest());

    }



    [TestMethod]

    public void ClassUnderTest_MethodUnderTest_TestHelperReturnsFalse()

    {

        var stub = new TestStub();

        stub.HelperMethodReturnValue = false;

        Assert.IsFalse(stub.MethodUnderTest());

    }

}
  

Вышесказанное выглядит нормально для простых вещей, однако класс-заглушка быстро становится экспоненциально больше и сложнее.
Я бы хотел заменить класс-заглушку с помощью Moq. Однако это не будет компилироваться, потому что по какой-то причине я не могу установить возвращаемое значение для защищенного метода.

 [TestMethod]

public void ClassUnderTest_MethodUnderTest_TestHelperReturnsFalse()

{

    var mockClass = new Mock<ClassUnderTest>();
    mockClass.Protected().Setup("HelperMethod").Returns(false);

    Assert.IsFalse(mockClass.Object.MethodUnderTest());

}
  

Кто-нибудь знает, как я бы это сделал? Могу ли я сделать это с помощью moq?

Комментарии:

1. Что-то здесь не так… вы не издеваетесь над своим SUT, вы издеваетесь над его зависимостями.

2. Да, на самом деле Yojin издевается над SUT, чтобы заменить часть SUT, которая должна храниться отдельно, а не в защищенном вспомогательном методе.

Ответ №1:

Глядя на исходный код moq, я бы предположил, что вам нужно явно вызвать общую версию Setup . Похоже, что не универсальная версия используется для методов void. Так что попробуйте

 mockClass.Protected().Setup<bool>("HelperMethod").Returns(false);
  

Помимо этого, я бы рекомендовал пересмотреть дизайн вашего класса. Если HelperMethod() выполняет такую кучу вещей, это стоило бы иметь собственный класс, который вводится как зависимость в ClassUnderTest. Тестирование макетного объекта вместо использования макетного объекта для тестирования чего-то «реального» — это не то, для чего созданы макетные фреймворки (по крайней мере, не в первую очередь).

Ответ №2:

Защищенные методы — не лучший способ изолировать зависимости, но иногда он встречается, особенно при адаптации устаревшего кода для тестируемости. Один из вариантов, который позволяет избежать неудобного синтаксиса Moq на основе строк, — это сделать метод «защищенным внутренним» (или просто «внутренним», если вы не собираетесь переопределять его при обычном использовании из других сборок.) Затем вы используете InternalsVisibleTo в сборке, чтобы предоставить метод. Это немного взлом, но использование защищенного метода для этой цели уже немного взлом. В некотором смысле я предпочитаю «внутренний» подход, поскольку он дает понять, что это бэкдор-метод, который вы не должны использовать (за исключением тестирования), в отличие от защищенного метода, который вы могли бы переопределить при обычном использовании.