En

JazzTeam Software Development Company

Agile Java Development

Data Driven Testing (содержит 7 примеров использования с рабочих проектов)

Введение

Data Driven Testing (DDT) - подход к созданию/архитектуре автоматизированных тестов (юнит, интеграционных, чаще всего применимо к backend тестированию), при котором тест умеет принимать набор входных параметров, и эталонный результат или эталонное состояние, с которым он должен сравнить результат, полученный в ходе прогонки входных параметров. Такое сравнение и есть assert такого теста. Притом как часть входных параметров, могут передаваться опции выполнения теста, или флаги, которые влияют на его логику.

        Часто покрываемая система, или метод являются очень сложными, и тогда невозможно ввести явно эталонные значения, здесь можно говорить об эталонных выходных состояниях. Бывает, нужно применить смекалку, чтобы понять, что же будет входным описанием, а что будет выходным. К примеру:

Преимущества использования

Особым плюсом хорошо-спроектированного DDT является возможность ввода входных значений и эталонного результата в виде, удобном для всех ролей на проекте - начиная от мануального тестировщика и заканчивая менеджером (тест менеджером) проекта, и даже, product owner-а (на практике автора такие случаи случались). Соответственно, когда Вы способны загрузить мануальных тестировщиков увеличением покрытия и увеличением наборов данных - это удешевляет тестирование. А также в целом, удобный и понятный формат позволяет более наглядно видеть, что покрыто, а что нет, это - по сути, и есть документация тестирования. К примеру, это может быть XLS файл с понятной структурой (хотя чаще всего properties файла достаточно). Смотрите пример 1.

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

Также, руководителю проекта становится легче контролировать разработку, ему не приходиться вникать в суть алгоритмов, и даже качества сделанного кода, DDT тестов вполне достаточно для продвижения вперёд.

Использование DDT, как ни странно, позволяет использовать на проектах менее квалифицированных инженеров, так как хорошее покрытие сложных участков с помощью DDT сразу же всё показывает, и человек за счёт Continuous Integration и прогонки тестов локально или на Dev окружении, имеет возможность самостоятельно фиксить внесённые проблемы.

Нюансы

Отмечу, что при определённом подходе в использовании, Robot Framework тесты могут быть очень DDT-ориентированными, и давать массу выгод.

Простейшая форма DDT - параметризованные тесты в JUnit, когда с помощью Data Providers в тест поставляются наборы данных для тестирования.

DDT - не панацея, но грамотное применение в нужных местах помогает очень и очень.

Как понять, когда нужно создавать DDT

Как создавать DDT

Чаще всего, достаточно сделать цикл над основным тестом, и организовать сравнение выходных данных и эталонных, а также реализовать репортинг - путём логгирования, или другим способом.

Но в нетривиальных случаях, возможно построение архитектуры вокруг необходимости создать DDT. Смотрите примеры 3 и 7.

Примеры

1. Самый простой, который автор использует для набора стажёров и практикантов

Задание: Перевод числа в цифровой записи в строковую. Например 134345 будет "сто тридцать четыре тысячи триста сорок пять". * Учесть склонения - разница в окончаниях (к примеру, две и два).

Вот как реализовал такой тест один из наших стажёров https://github.com/Dubouski/NumbersToWords/ - обратите внимание, он решил наборы данных для тестирования положить в эксель, то есть любой, в том числе, мануальный тестировщик, сможет тестировать его алгоритм.

2. Пример, который автор часто приводит на лекциях

Задание: Реализовать HTML парсер с нуля. То есть реализовать перевод строки в DOM модель.

Один инженер будет идти таким путём. Он попросит время, чтобы вычитать спецификацию. Потом попросит подумать над архитектурой. Потом будет делать прототипы. Как-то их тестировать. Всё это время, дни, недели, менеджер и команда не смогут проверять его статус на деле.

Второй инженер в первый же день сделает первый unit test, который будет проверять самый простой случай - пустую строку, или же, пустой тег <html/>, или что-то ещё простое. И каждый день он будет накидывать новые состояния, расширяя свой код. Логично это будет обернуть в DDT, и позволить всей команде, менеджерам и тестировщикам накидывать разные варианты HTML, а также эталонного результата (например, дерево DOM объектов, которые могут записываться первым прогоном этого алгоритма и верифицироваться вручную). В таком подходе накапливаются кейсы, изменения в алгоритме и логике не валят предыдущие наборы данных, и появляется возможность чётко понимать, что именно уже реализовано. Более того, наборы входных данных и эталонные значения можно разбить на папки, подпапки, и таким образом создать документацию парсеру.

3. Подход, который применяется в тестировании платформы для автоматизации тестирования XML2Selenium

XML2Selenium - система, построенная на плагинах и на взаимодействии плагинов. Плагины генерируют ивенты, подписываются на ивенты других плагинов. То есть от пользователя скрыто сложное взаимодействие.

XML2Selenium умеет прокрутить тесты, записанные в формате XML, и тестировать Web UI приложение (внутри мы используем Selenium/Web Driver).

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

Мы применили такой подход. Был введён специальный JVM параметр, который просил ядро сохранять всё дерево вызовов и ивентов для данного теста, включая регистрацию, инициализацию, и весь жизненный цикл плагинов, а также контекст, передаваемый между плагинами после каждого атомарного действия теста. Таким образом, мы получали сгенерённый файл, который содержал полный слепок поведения системы, вот пример такого файла:

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

4. Подход, который применялся на одном из проектов к тестированию выгрузки данных

В этом случае на проекте требовалось протестировать, насколько правильно работал компонент выгрузки данных. Выгрузка была нетривиальная, зависела от состояния базы данных, и в ней то и дело всплывали баги. Можно было сказать, что команда не контролировала стабильность этого компонента. Требовалось радикально увеличить его стабильность.

Решение было в использовании DDT. Входным параметром выступал дамп базы данных в формате XML. Эталонным значением - ранее проверенный файл выгрузки, заведомо безошибочный. Таким образом, тестирование свелось к созданию разных версий базы данных, а также к проверке эталонных файлов, и созданию таких тестовых наборов.

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

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

5. Реальный проект - тестирование обработки ошибок в сложной грамматике

Суть проекта была следующей. На клиентскую часть от сервера приходил XML с полным описанием не только UI, который нужно было отрисовать, но и с поведением, которое передавалось в бинарном формате в этом же XML. То есть поведение могло быть каким угодно, налицо плагинная система.

Данный XML имел достаточно сложный синтаксис, и наша команда была независима от разработчиков backend. Нам необходимо было обезопасить себя от неправильных XML, мы должны были начинать построение UI только тогда, когда 100% понимали, что XML правильный.

Для этого был использован DDT. Мы создали огромное количество вариантов входного XML, в том числе, неправильных, некорректных, и проверяли, что выбрасываются нужные исключения с нужными сообщениями.

Таким образом, входной параметр - это XML, эталонное состояние - тип исключения, его сообщение, или даже, часть stack trace. Было создано несколько сотен тестовых наборов, и каждый раз, когда менялось что-то в формате, появлялись новые параметры, или же, появлялись новые исключения и баги - тесты расширялись. Под окончание проекта это была самая стабильная часть системы.

На этом изображении приведена часть Excel файла, в котором задаётся входной параметр и эталонное поведение - в нашем случае, информация об исключениях (тип и сообщение).

6. Тестирование сложной загрузки данных. Изолирование логики хранимых процедур

На одном из проектов происходит загрузка данных в формате CSV (очень сложного внутреннего формата) в базу данных. Притом нам достался legacy код, и вся логика загрузки происходит в хранимых процедурах, в которых порядка десятка тысяч строчек. Задача состояло в том, чтобы стабилизировать этот компонент выгрузки, и обеспечить регрессионное тестирование.

Как и в примере 4. с выгрузкой данных, мы применили DDT, и в качестве входных параметров использовали 1) состояние базы данных (мы использовали дамп в формате SQL) и 2) файл, который нужно будет загрузить. В качестве эталонного значения мы использовали XLS файл, который представлял содержимое нужных нам таблиц этой базы данных после загрузки.

Все баги, которые часто случались, мы помещали в DDT.

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

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

7. Наиболее сложный пример DDT из практики автора

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

Для реализации этой задачи был создан фреймворк, позволяющий по описанию системы разворачивать её - в реальности это разные виртуальные сервера, которые связаны друг с другом через нашу SOA платформу. На каждом из серверов было развёрнуто приложение, которое было подключено к системной шине, и умело выполнять скрипты, которые ему поступали. В передаваемых скриптах мы зашивали требуемое поведение и требуемый сценарий.

Таким образом, входными данными были 1) файл описания топологии системы и 2) набор скриптов для каждого узла этой топологии, которые говорили, что происходит на каждом из узлов, а эталонными значениями было дерево (своеобразный лог) обработки сообщений в такой системе.  

 

, , , , , , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *