Тестирование, управляемое через данные, на C# с NUnit и RestSharp |
16.04.2020 00:00 |
Автор: Баз Джикстра (Bas Dijkstra) Ранее я приводил примеры базовых тестов на C# для REST API с использованием NUnit и библиотеки RestSharp. В этой статье я хочу поговорить об этом подробнее, показав, как сделать эти тесты управляемыми через данные. Если вы не знаете, что имеется в виду под "управлением через данные": если я хочу запускать тесты, использующие одинаковую логику или поток в тестируемом приложении несколько раз с различными комбинациями входных значений и соответствующими ожидаемыми результатами, я называю это тестированием, управляемым через данными. Это особенно полезно при тестировании REST API, так как в их основе лежит отправка и получение данных, а также открытие бизнес-логики другим уровням архитектуры приложения (например, графическому интерфейсу пользователя) или другим приложениям (пользователям API). Для начала рассмотрите эти тесты, написанные на RestSharp и NUnit: [TestFixture] ); Отметьте, что тип LocationResponse – кастомный тип, который я самостоятельно определил. См. репозиторий GitHub для его внедрения. Эти тесты – хороший пример того, о чем я писал ранее: я применяю одинаковую логику (получение данных о локации на основании страны и индекса, и дальнейшая верификация соответствующего названия местности из ответа API) трижды с разными наборами данных. Это быстро становится неэффективным при добавлении тестов или комбинаций тестовых данных, и в результате у вас будет много дублирующегося кода. К счастью, NUnit дает несколько возможностей сделать эти тесты управляемыми через данные. Давайте детально рассмотрим две из них. Использование атрибута [TestCase] Первый способ создания тестов, управляемых через данные – это использование атрибута [TestCase], предоставляемого NUnit. Вы можете добавлять несколько атрибутов [TestCase] к одному и тому же тест-методу, и уточнить комбинации входных и ожидаемых выходных параметров, которыми метод должен пользоваться. Плюс к этому вы можете уточнять другие характеристики для индивидуальных тест-кейсов. Одна из наиболее полезных – это свойство TestName, которое можно использовать для читабельного и полезного имени конкретного кейса. Это имя также отражается в отчете, поэтому я очень рекомендую вам задавать его. Вот как будет выглядеть наш код после рефакторинга с использованием атрибута [TestCase]: [TestFixture] Намного лучше! Теперь нам нужно определить логику теста только один раз, и NUnit сам проведет итерации согласно значениям, определенным в атрибутах [TestCase]: Однако у этого подхода есть несколько недостатков:
И тут в игру вступает атрибут [TestCaseSource]. Использование атрибута [TestCaseSource] Если вам желательно или необходимо работать с большим количеством комбинаций тестовых данных, и/или вы хотите иметь возможность задавать тестовые данные вне тест-класса, использование [TestCaseSource] может быть полезной опцией. В этом случае вы задаете или считываете тестовые данные в отдельном методе, который затем передается в исходный тест-метод. NUnit самостоятельно запустит итерации различных комбинаций тестовых данных, возвращенных методом, передающим эти данные. Вот пример применения [TestCaseSource] к нашим тестам: [TestFixture] В этом примере мы определяем наши тестовые данные в отдельном методе LocationTestData(), а затем говорим тестовому методу использовать этот метод в качестве источника тесовых данных, используя атрибут [TestDataSource], который берет в качестве аргумента название метода тестовых данных. Для ясности, тестовые данные все еще жестко закодированы в теле метода LocationTestData(), но это необязательно. Вы можете с той же легкостью написать метод, считывающий тестовые данные из любого внешнего источника – лишь бы он оставался статичным и возвращал объект типа IEnumerable, или любой объект, использующий этот интерфейс. К тому же благодаря тому, что атрибуты [TestCase] и [TestCaseSource] принадлежат NUnit, а не RestSharp, вы можете применять принципы, проиллюстрированные в статье, и к другим типам тестов. Однако будьте осторожны перед использованием их для тестирования через пользовательский интерфейс при помощи инструментов вроде Selenium WebDriver. С шансами вы попадете в классическую ловушку "только то, что мы это можем, не значит, что мы должны". Я считаю, что тестирование, управляемое через данные, при помощи Selenium WebDriver – это код с душком: если вы множество раз проходите через одинаковую последовательность экранов, и вся разница в них в тестовых данных, то с высокой вероятностью есть более эффективный способ тестирования той же самой бизнес-логики (к примеру, через API). Крис МакМахон объясняет это куда изящнее в своей статье. Крайне рекомендуется к прочтению. Для других типов тестирования (API или юнит) тестирование на основе данных может быть отличным способом улучшить поддерживаемость кода ваших тестов, и сделать его более мощным. Все примеры кода из этой статьи можно найти на странице GitHub. |