Как вы пишете тесты для кода, который зависит от конкретных внешних реализаций, которые нельзя смоделировать?

17

Справочная информация: я думаю о том, чтобы попытаться представить концепцию модульных тестов моим коллегам, создав некоторые из них для модуля, над которым я работал; требования к нему недавно изменились и требуют дополнительных абстракций / взаимодействий, поэтому кажется хорошим способом разработать набор тестов, которые «докажут», что это работает, без необходимости вручную разбираться с приложением.

Проблема, однако, заключается в том, что модуль опирается на немодальные внешние факторы, а именно PDF и XSL. По сути, я читаю XML из базы данных и применяю к нему XSL-преобразование, а затем преобразую его в PDF-файл, используя библиотеку ABCPDF. Этот PDF затем объединяется с другим PDF на основе статического шаблона. Я знаю, что могу проверить XML и убедиться, что значения верны, но многие потенциальные ошибки и проблемы связаны с фактическим отображением готового документа - например, мелочами, такими как длина текстовых строк, где определенные области HTML расположен ли он относительно документа и т. д. Можно ли вообще протестировать эти вещи (я понимаю, что это, вероятно, интеграционные тесты или ... третий тип теста, название которого я забыл [не приемочные тесты, другой вид], а не единица) тесты), поскольку я не могу, насколько мне известно, легко смоделировать PDF-файл, за исключением того, что он затем создается, затем читается обратно или создается строка HTML (т.е. преобразованный XML) и анализируется вручную, чтобы проверить наличие определенных ячеек таблицы в отношение к другим ячейкам таблицы.

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

Уэйн Молина
источник
6
«немодные внешние факторы» - это намек на то, что вы в первую очередь не проводите юнит- тесты. Это означает интеграционное тестирование. Что вы хотите сделать? Модульное тестирование вашего кода или интеграционное тестирование этой составной вещи? Пожалуйста, выберите один или другой, потому что трудно говорить об обоих одновременно.
S.Lott
2
Я не покупаю "unmockable". Я приму «что я не знаю, как издеваться», что просто означает, что ваш настоящий вопрос «как я это высмеиваю?».
Рейн Хенрикс
Вероятно :) Я знаком с насмешками над используемым XML, но не с тем, как насмехаться над реальным PDF или HTML документом, где форматирование имеет значение
Уэйн Молина
1
Я думаю, что вы имеете в виду «функциональные» (от конца до конца приложения) или «системные» (от нескольких приложений до конца) тесты
Гэри Роу
@ Гэри - Да, слово «функционал». Я помню их сейчас из изучения Rails: модели модульных тестов, контроллеры функциональных тестов, тесты интеграции все.
Уэйн Молина

Ответы:

13

Проверьте функцию не устройство

Используя известные XML-данные, выведите PDF-файл и вручную (и тщательно) убедитесь, что он правильный. Затем сохраните его как ссылку.

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

Если сравнение на уровне файлов является неудовлетворительным, отобразите PDF в конце теста и сделайте снимки экрана, а затем сравните автоматические тесты со справочными снимками экрана.

Стивен А. Лоу
источник
+1, потому что вы заинтересованы только в конечном результате на этом уровне. Если вы измените реализацию того, как вы получили PDF, вам не нужно менять функциональный тест.
Гэри Роу
2
+1 за добрый совет, это то, что мы делаем в нашем текущем проекте. Мы создали собственный набор инструментов для сравнения PDF, что позволяет нам не вносить изменения в документы, такие как временные метки. Предостережение: переключение на другой (версию) рендерера PDF может привести к незначительным изменениям макета, что приведет к прямому двоичному сравнению, чтобы выявить кучу ложных срабатываний.
Петер Тёрёк
5

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

public class PdfBuilder : IPdfBuilder
{
  public byte[] BuildPdf(...)
  {
    // actual untestable code here
  }
}

public interface IPdfBuilder
{
  byte[] BuildPdf(...);
}

Затем вы можете смоделировать IPdfBuilder в своих тестах, чтобы делать все, что вы хотите. Это часто означает, что вам нужно начать использовать контейнер IoC ( /programming/871405/why-do-i-need-an-ioc-container-as-oppposed-to-straightforward-di-code и /programming/21288/which-net-dependency-injection-frameworks-are-worth-looking-into как место для начала), если вы не используете его сейчас.

И тесты, которые не являются юнит-тестами, часто называют интеграционными тестами. Сложные интеграционные тесты зачастую не стоят того, поэтому вы просто абстрагируете эту часть и сокращаете объем бизнес-логики в этой абстракции, чтобы можно было протестировать ее в модульном тесте.

Дайте мне знать, если это не совсем понятно.

Travis
источник
+1 за сокрытие непроверяемого кода. Затем вы можете выполнять ручные тесты, пока не решите, что нужно пересечь этот интерфейс, чтобы получить правильный результат, и выполнить модульный тест для этого , чтобы получить ваши регрессионные модульные тесты.
Этель Эванс
1

Некоторое время назад я построил нечто очень похожее и просто использовал базовые визуальные тесты. Тестирование не обязательно должно быть автоматизированным, поэтому нет ничего плохого в том, чтобы просто искать ожидаемый результат (очевидно, во множестве предопределенных ситуаций). Зачастую картинка стоит тысячи тестов, связанных с визуальными эффектами . Я широко использую автоматическое модульное тестирование, но я думаю, что некоторые люди могут немного увлечься, когда переходят на тестирование GUI или что-то визуальное ИМХО. С определенным продуктом я признаю, что этого «достаточно хорошего» подхода будет недостаточно, поэтому YMMV.

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

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