TDD Mocking - Определяет ли поведение белого объекта при тестировании поведения ложного объекта? - PullRequest
9 голосов
/ 19 февраля 2010

Я действительно недавно попал в TDD, и после прочтения книги Кента Бека «Разработка через тестирование» у меня все еще остается много вопросов о разработке тестов.

Одна из проблем, с которыми яв настоящее время есть использование объектов Mock.Ниже приведен очень простой пример создания отчета:

public string MakeFinancialReport()
{
    return sys1.GetData() + sys2.GetData() + sys3.GetData();
}

Отчет должен содержать заголовок, текст и нижний колонтитул.Итак, быстрый тест, чтобы увидеть, существуют ли эти заголовки в отчете:

public void TestReport()
{
    string report = MakeFinancialReport();
    Assert.IsTrue(report.Contains("[Title]") && report.Contains("[Body]") && report.Contains("[Footer]"));
 }

Чтобы изолировать метод, я думаю, я бы прогонял вызовы sys1, sys2 и sys3.Теперь, если они все издеваются, что мне осталось проверить?Кроме того, когда я имитирую их, почему я должен сказать имитируемым объектам, что они будут вызываться один раз и вернуть X и т. Д. Если это не просто тест черного ящика, а MakeFinancialReport может сделать столько вызовов, сколькоон хочет создать отчет?

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

Ответы [ 4 ]

4 голосов
/ 19 февраля 2010

Мартин, я думаю, вы должны использовать mocks для sys1-3, но они должны быть достаточно простыми, чтобы возвращать одну символьную строку каждый.

Это означает, что ваш тест должен выглядеть следующим образом:

public void TestReport()
{
    // Setup mocks for sys1-3
    string report = MakeFinancialReport();
    Assert.IsTrue(report.equals("abc"));
}

Это показывает, что MakeFinancialReport имеет свойства, которые он вызывает GetData() из sys1-3 и , он объединяет результаты в этом конкретном порядке.

3 голосов
/ 19 февраля 2010

В сущности, MakeFinancialReport практически ничего не делает, кроме взаимодействия с более интересными соавторами, и, вероятно, не стоит проводить модульное тестирование.

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

"Определяет ли тестирование белого поля поведения ложного объекта?" Безусловно - если у вашего класса есть зависимость, над которой вы издеваетесь, вы привязываете свой тест к этой зависимости. Но у тестирования белого ящика есть свои преимущества. Не все взаимодействия с коллаборационистами так же тривиальны, как и в вашем примере.

2 голосов
/ 19 февраля 2010

Вы должны использовать только фиктивные объекты, когда они полезны. Если MakeFinancialReport, sys1, sys2 и sys3 все имеют сложную логику, то вы захотите проверить каждый из них независимо. Давая фиктивные версии трех sysX объектов MakeFinancialReport, вы убираете их сложность и просто тестируете MakeFinancialReport.

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

Когда вы говорите о том, что вам не нужно устанавливать явные ожидания и возвращаемые значения, это связанная концепция, называемая заглушками. Вы можете найти полезными слова Мартина Фаулера " Насмешки не заглушки ".

Книга Кента Бека - отличное введение, но если вам нужны подробности, я настоятельно рекомендую книгу xUnit Patterns . Например, в нем есть раздел о фиктивных объектах , а также более общая категория двойных тестов .

0 голосов
/ 19 февраля 2010

Я думаю, что одна из проблем заключается в том, что ваш тест смешивает обязанности sys1, sys2 и sys3 с обязанностями метода TestReport.Мне кажется, что вы должны разделить свои тесты на 2 части:

1) MakeFinancialReport () возвращает объединение sys1, sys2, sys3.Там вы можете заглушить sys1 и т. Д. С чем-то вроде

var sys1 =MockRepository.GenerateStub<ISys>();
sys1.Expect(s=>s.GetData()).Return("Part 1");
// etc... for sys2, sys3 var
reportMaker = new ReportMaker(sys1,sys2, sys3); 
Assert.AreEqual("Part 1" + "Part 2" + "Part 3", reportMaker.MakeFinancialReport();

Класс, которому принадлежит метод MakeFinancialReport (), не должен заботиться или знать, что делают классы sys.Они могут вернуть любой класс - MakeFinancialReport () просто объединяет, это то, что вы должны проверить (если считаете, что оно того стоит).

2) Протестируйте метод GetData () из интерфейса sys1, sys2, sys3 Implement.Это, вероятно, когда вы бы проверили, в каких обстоятельствах вы ожидаете увидеть «Тело», «Заголовок» и т. Д.(3 экземпляра sys) и четкое разделение того, что делает sys, и что делает MakeFinancialReport.

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

...