Модульное тестирование с Moq - PullRequest
6 голосов
/ 07 июня 2011

Допустим, у меня есть простой класс с несколькими функциями:

public class MyClass
{
    public int GetTotal(int myValue, string myString)
    {
        if (myValue > 10)
            return GetTotal(myValue);
        else
            return GetTotal(myString);
    }

    public int GetTotal(int myValue)
    {
        return myValue * 25 / 12 + 156;
    }

    public int GetTotal(string myString)
    {
        return myString.Length * 25 / 48 + 27;
    }
}

Я хотел бы провести модульное тестирование моей первой функции и «смоделировать» другие, int GetTotal(int myValue) и int GetTotal(string myString), чтобы проверить только код внутри основной функции. Я использую Moq в качестве основы для насмешек. Существуют ли уловки, которые позволили бы мне получить код из функции, которую я хочу протестировать, и смоделировать внутренний вызов других функций? Или мне нужно вызвать второй объект, подобный этому, чтобы все высмеивать?

public class MyCalculator
{
    public int GetTotal(int myValue)
    {
        return myValue * 25 / 12 + 156;
    }

    public int GetTotal(string myString)
    {
        return myString.Length * 25 / 48 + 27;
    }
}

public class MyClass
{
    MyCalculator _Calculator;

    public MyClass(MyCalculator calculator) { _Calculator = calculator; }
    public int GetTotal(int myValue, string myString)
    {
        if (myValue > 10)
            return _Calculator.GetTotal(myValue);
        else
            return _Calculator.GetTotal(myString);
    }
}

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

Обновление

Ложная реализация ответа Томаса:

public class MyClass
{
    public int GetTotal(int myValue, string myString)
    {
        if (myValue > 10)
            return GetTotal(myValue);
        else
            return GetTotal(myString);
    }

    public virtual int  GetTotal(int myValue)
    {
        return myValue * 25 / 12 + 156;
    }

    public virtual int GetTotal(string myString)
    {
        return myString.Length * 25 / 48 + 27;
    }
}

[TestClass]
public class Test
{
    [TestMethod]
    public void MyClass_GetTotal()
    {
        Mock<MyClass> myMockedClass = new Mock<MyClass>() {CallBase = true};

        myMockedClass.Setup(x => x.GetTotal(It.IsAny<int>())).Returns(1);
        myMockedClass.Setup(x => x.GetTotal(It.IsAny<string>())).Returns(2);

        var actual = myMockedClass.Object.GetTotal(0,string.Empty);

        Assert.AreEqual(2,actual);
    }
}

Обновление 2

См. Также ответ Гишу для более глобального взгляда на этот «вопрос».

Ответы [ 2 ]

3 голосов
/ 07 июня 2011

Конечно! В Rhino Mocks вы можете использовать частичный макет именно для этой цели. Создайте новый макет, как это:

var mock = MockRepository.GeneratePartialMock<MyClass>();

Затем вы можете смоделировать или заглушить те два метода, которые вы не хотите называть так:

mock.Stub(x => x.GetTotal(10)).Return(42);

Требуется, чтобы ваши методы GetTotal были виртуальными.

2 голосов
/ 23 августа 2011

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

Так что, если A зависит от B, а B трудно контролировать / ощущать ИЛИ просто медленно. Мы ставим B за роль R, а затем макет R для юнит-тестирования A.

В вашем случае ваш A.Method1 внутренне вызывает Method2 или Method3, которые, как я полагаю, не являются недружественными к тестированию. ИМХО изолировать М1 от М2 и М3 - слишком дорого для небольшой выгоды (если я не понимаю некоторые контекстные детали). Если завтра вы решите изменить метод Method1 для использования M4 вместо M3, ваши тесты не пройдут, даже если M1 сделает то, что должен (из функциональности p.o.v.)

Вы можете напрямую написать тесты для Method1, не сообщая тестам подробности реализации (которые он вызывает M2 и M3). Поэтому мой комментарий заключался в том, что изоляция объектов иногда бывает полезна, но изоляция функций одного и того же объекта друг от друга - ИМХО излишне.

...