Как сделать интеграционное тестирование in.NET с настоящими файлами?
У меня есть несколько классов, которые реализуют некоторую логику, связанную с файловой системой и файлами. Например, я выполняю следующие задачи в рамках этой логики:
- проверка, если определенная папка имеет определенную структуру (например. она содержит подпапки с конкретными именами и т. д...)
- загрузка некоторых файлов из этих папок и проверка их структуры (например. это некоторые конфигурационные файлы, расположенные в определенном месте в определенной папке)
- загрузить дополнительные файлы для тестирования/проверки из файла конфигурации (например. этот конфигурационный файл содержит информацию о других файлах в той же папке, которые должны иметь другую внутреннюю структуру и т. д...)
теперь вся эта логика имеет некоторые документооборота и исключения, если что-то не так (например. файл конфигурации не найден в конкретную папку). Кроме того, есть управляемых (MEF) участвует в этой логике, потому что некоторые из этих файлы, которые я проверяю, являются управляемыми DLL, которые я вручную загружаю в агрегаты MEF и т. д...
теперь я хотел бы проверить все это каким-то образом. Я думал о создании нескольких физических тестовых папок на жестком диске, которые охватывают различные тестовые случаи, а затем запускают мой код против них. Я мог бы создать, например:
- папка с правильной структурой и все файлы действительны
- папка с правильной структурой, но с недопустимым конфигурационным файлом
С правильная структура, но отсутствует файл конфигурации
так далее...
будет ли это правильным подходом? Я не уверен, хотя, как именно запустить мой код в этом сценарии... Я, конечно, не хочу запускать все приложение и указывать его, чтобы проверить эти издевательские папки. Должен ли я использовать некоторую платформу модульного тестирования для написания "модульных тестов", которые выполняют мой код против этих объектов файловой системы?
В общем, все это правильный подход для такого рода тестирования сценарии? Есть ли другие лучшие подходы?
5 ответов:
в первую очередь, Я думаю, это лучше писать модульные тесты, чтобы проверить свою логику, не касаясь каких-либо внешних ресурсов. Здесь у вас есть два варианта:
- вы должны использовать уровень абстракции, чтобы изолировать свою логику от внешних зависимостей, таких как файловая система. Вы можете легко заглушить или издеваться (вручную или с помощью ограниченных рамок изоляции, таких как NSubstitute, FakeItEasy или Moq) эти абстракции в модульных тестах. Я предпочитаю этот вариант, потому что в этом случае тесты подталкивают вас к лучшему дизайну.
- если вам приходится иметь дело с устаревшим кодом (только в этом случае), вы можете использовать один из неограниченных фреймворков изоляции (таких как Typemock Isolator, JustMock или Microsoft Fakes), которые могут заглушать/имитировать почти все (например, запечатанные и статические классы, невиртуальные методы). Но они стоят денег. Единственным "бесплатным" вариантом является Microsoft Fakes, если вы не являетесь счастливым владельцем Visual Studio 2012/2013 Премиум И Ultimate.
в модульных тестах вам не нужно проверять логику внешних библиотек, таких как MEF.
во-вторых, Если вы хотите написать интеграционные тесты, затем вам нужно написать тест "счастливый путь" (когда все в порядке) и некоторые тесты, которые проверяют вашу логику в граничных случаях (файл или каталог не найден). В отличие от @Sergey Berezovskiy, я рекомендую создать отдельные папки для каждого тестового случая. Основное преимуществами является:
- вы можете дать вашей папке значимые имена, которые более четко выражают ваши намерения;
- вам не нужно писать сложную (т. е. хрупкую) логику настройки/демонтажа.
- даже если вы решите позже использовать другую структуру папок, то вы можете изменить ее более легко, потому что у вас уже будет рабочий код и тесты (рефакторинг под тестовым жгутом намного проще).
как для модульных, так и для интеграционных тестов,вы можно использовать обычные рамки модульного тестирования (как NUnit или xUnit.NET). С помощью этой рамки очень легко запускать ваши тесты в сценариях непрерывной интеграции на сервере сборки.
Если вы решили оставить оба вида тестов, то вам нужно отделить модульные тесты от интеграционных тестов (вы можете создать отдельные проекты для каждого вида испытаний). Причины этого:
- тесты сеть безопасности для разработчиков. Они необходимо обеспечить быструю обратную связь об ожидаемом поведении системных блоков после последних изменений кода (исправления ошибок, новые функции). Если они выполняются часто, то разработчик может быстро и легко идентифицировать фрагмент кода, который сломал систему. Никто не хочет запускать медленные модульные тесты.
- интеграционные тесты обычно медленнее, чем юнит-тесты. Но у них разные цели. Они проверяют, что блоки работают, как ожидалось, с реальными зависимостями.
вы должны проверить как можно больше логики с помощью модульных тестов, абстрагируя вызовы файловой системы за интерфейсами. Использование инъекции зависимостей и платформы тестирования, такой как FakeItEasy позволит вам проверить, что ваши интерфейсы фактически используется/вызывается для работы с файлами и папками.
однако в какой-то момент вам придется протестировать реализации, работающие в файловой системе, и именно здесь вам понадобится интеграция тесты.
то, что вам нужно проверить, похоже,относительно изолированной так как все, что вы хотите проверить, это ваши собственные файлы и каталоги, на вашей собственной файловой системе. Если вы хотите протестировать базу данных или другую внешнюю систему с несколькими пользователями и т. д., Все может быть сложнее.
Я не думаю, что вы найдете какие-либо "официальные правила" для того, как лучше всего делать интеграционные тесты этого типа, но я считаю, что вы на правильном пути. Некоторые идеи, которые вы должны стремиться навстречу:
- четкие стандарты: сделайте правила и цель каждого теста абсолютно ясными.
- автоматизация: возможность повторного запуска тестов быстро и без слишком большой ручной настройки.
- Повторимость: тестовая ситуация, которую вы можете "сбросить", поэтому вы можете быстро выполнить тесты с небольшими изменениями.
создать повторяемый тест-сценарий
в вашей ситуация, я бы установил две основные папки: одна, в которой все так, как должно быть (т. е. работает правильно), и одна, в которой все правила нарушены.
Я бы создал эти папки и любые файлы в них, а затем заархивировал каждую из папок и написал логику в тестовом классе для распаковки каждого из них.
на самом деле это не тесты; думайте о них вместо этого как о "скриптах" для настройки вашего тестового сценария, позволяя вам удалять и воссоздавать ваши папки и файлы легко и быстро, даже если ваши основные интеграционные тесты должны измениться или испортить их во время тестирования. Причина для размещения их в тест-классе, это просто сделать то легко запустить из того же интерфейса, как вы будете работать с во время тестирования.
тестирование
создать два набора тестовых классов, один набор для каждой ситуации (правильно настроить папку против папки с нарушенными правилами). Поместите эти тесты в иерархию папок, которая кажется вам значимой (в зависимости от сложность вашей ситуации).
неясно, насколько вы знакомы с модульным/интеграционным тестированием. В любом случае, я бы рекомендовал Нанит. Мне нравится использовать расширения в
Shouldкак хорошо. Вы можете получить оба из них от Nuget:install-package Nunit install-package Shouldshould-package позволит вам написать тестовый код следующим образом:
someCalculatedIntValue.ShouldEqual(3); someFoundBoolValue.ShouldBeTrue();обратите внимание, что есть несколько тест-бегунов доступны, чтобы запустить тесты С. Я лично у меня был только реальный опыт работы с бегуном, встроенным в Resharper, но я вполне доволен этим, и у меня нет проблем с его рекомендацией.
Ниже приведен пример простого тест-класса с двумя тестами. Обратите внимание, что в первом случае мы проверяем ожидаемое значение с помощью метода расширения от Should, в то время как во втором мы явно ничего не тестируем. Это связано с тем, что он помечен тегом [ExpectedException], что означает, что он не будет работать, если исключение указанного типа не выбрасывается при выполнении теста. Это можно использовать для проверки того, что при нарушении одного из правил создается соответствующее исключение.
[TestFixture] public class When_calculating_sums { private MyCalculator _calc; private int _result; [SetUp] // Runs before each test public void SetUp() { // Create an instance of the class to test: _calc = new MyCalculator(); // Logic to test the result of: _result = _calc.Add(1, 1); } [Test] // First test public void Should_return_correct_sum() { _result.ShouldEqual(2); } [Test] // Second test [ExpectedException(typeof (DivideByZeroException))] public void Should_throw_exception_for_invalid_values() { // Divide by 0 should throw a DivideByZeroException: var otherResult = _calc.Divide(5, 0); } [TearDown] // Runs after each test (seldom needed in practice) public void TearDown() { _calc.Dispose(); } }со всем этим на месте, вы должны быть в состоянии создавать и воссоздавать тестовые сценарии, а также запускать тесты на них в простой и воспроизводимый способ.
Edit: как отметил в комментарии утверждать.Throws () - это еще один вариант для обеспечения исключения выдаются по мере необходимости. Лично Я как и тег-вариант, хотя и с параметрами, вы можете проверить такие вещи, как сообщение об ошибке там тоже. Другой пример (предполагая, что пользовательское сообщение об ошибке выбрасывается из вашего калькулятора):
[ExpectedException(typeof(DivideByZeroException), ExpectedMessage="Attempted to divide by zero" )] public void When_attempting_something_silly(){ ... }
Я бы пошел с одной тестовой папкой. Для различных тестовых случаев вы можете поместить разные допустимые/недопустимые файлы в эту папку в рамках установки контекста. В тестовом демонтаже просто удалите эти файлы из папки.
Е. Г. с Specflow:
Given configuration file not exist When something Then foo Given configuration file exists And some dll not exists When something Then barопределите каждый шаг настройки контекста как копирование / не копирование соответствующего файла в папку. Вы также можете использовать стол для определения, какой файл должен быть скопирован в папку:
Given some scenario | FileName | | a.config | | b.invalid.config | When something Then foobar
Я не знаю архитектуру вашей программы, чтобы дать хороший совет, но я буду стараться
- Я верю, что ты не нужно тестировать реальную структуру файлов. Службы доступа к файлам определяются системой / платформой, и их не нужно тестировать. Вам нужно издеваться над этими службами в связанных тестах.
- Также вам не нужно тестировать MEF. Это уже проверено.
- использовать твердые принципы для проведения модульных тестов. Особенно взгляните на Принцип Единой Ответственности это позволит вам создавать модульные тесты, которые не будут связаны друг с другим. Просто не забывайте о насмешках, чтобы избежать зависимостей.
- чтобы сделать интеграционные тесты, вы можете создать набор вспомогательных классов, которые будут эмуляция сценариев файловых структур, который вы хотите проверить. Это позволит вам оставаться не прикрепленным к машине, на которой вы будете выполнять эти тесты. Такие подход может быть сложнее, чем создание реальной файловой структуры, но мне это нравится.
Я бы построил логику фреймворка и проверил проблемы параллелизма и исключения файловой системы, чтобы обеспечить четко определенную среду тестирования.
попробуйте перечислить все границы проблемной области. Если их слишком много, то рассмотрите возможность того, что ваша проблема слишком широко определена и должна быть разбита. Каков полный набор необходимых и достаточных условий, необходимых для того, чтобы ваша система прошла все тесты? Затем посмотрите на каждое состояние и рассматривайте его как индивидуальное точка атаки. И перечислите все способы, которые вы можете придумать, чтобы нарушить это. Попробуйте доказать себе, что вы нашли их всех. Затем напишите тест для каждого.
Я бы сначала прошел этот процесс для среды, построил и протестировал его сначала до удовлетворительного стандарта, а затем для более подробной логики в рабочем процессе. Некоторые итерации могут потребоваться, если во время тестирования возникают зависимости между средой и детальной логикой.
Comments