Структура тестового фреймворка |
30.08.2023 00:00 |
Автор: Валентин Агапитов Любой автоматизатор тестов рано или поздно сталкивается с задачей либо дополнить, либо расширить тестовый фреймворк. К тому же, у многих есть профессиональная цель написать свой тестовый фреймворк. Чтобы реализовать это, необходимо знать и понимать архитектуру тестовых фреймворков, так как от заложенной архитектуры зависит стабильность, расширяемость и гибкость вашего фреймворка и тестов в целом. Всем известная организация ISTQB разработала общую схему (архитектуру) компонентов, из которых должен состоять тестовый фреймворк. В этой статье разберем, что это за компоненты и для чего они нужны. Так как эта схема включает еще и окружение, с которым должен взаимодействовать фреймворк, что выходит за рамки статьи, то ограничимся разбором только архитектуры тестового фреймворка, т.е. центральной части схемы. Дисклеймер
Test Adaptation Layer (слой тестовых адаптеров)Начнём с самого нижнего уровня - Test Adaptation Layer. Слой тестовых адаптеров обеспечивает взаимодействие с тестируемой системой. При анализе системы, принимается решение через какие интерфейсы и обходные пути тесты будут посылать и получать данные, изменять состояние системы и т.д. Далее, все вызовы оборачиваются в адаптеры, чтобы абстрагироваться от тестируемой системы и протоколов взаимодействия с ней. К таким интерфейсам, обычно, относят технические протоколы: HTTP, SSH и т.д., и GUI технологии: Selenium (Playwright), Mobile, Desktop, Image Recognition и т.д. Что же такое тестовый адаптер? Это такие функции, методы или классы, которые оборачивают вызов базовых библиотек и предоставляют упрощенный интерфейс взаимодействия. Адаптер должен быть:
Если говорить о примерах, то к адаптерам можно отнести класс BaseRequest и более редкий BaseResponse. Это такие классы-обертки над базовым request и объектом response, которые облегчают работу с HTTP, позволяют упростить взаимодействие с библиотекой и использовать логирование в одном общем месте, а не копировать его из теста в тест. Еще один пример адаптера - это PageObject. По сути он оборачивает работу с браузером в класс, который содержит методы с вызовом базовых функций Selenium (Playwright) для работы со страницей.
Если суммировать, то есть некие библиотеки, над которыми делаются обертки. Тем самым, упрощается интерфейс взаимодействия с базовыми библиотеками. Также, реализация адаптеров не должна зависеть от самого фреймворка и не должно быть смешение логик. В результате, адаптеры можно переиспользовать в другом фреймворке. Test Execution Layer (слой исполнения/запуска тестов)Рассмотрим второй уровень в типовом фреймворке автотестирования. Слой исполнения/запуска тестов - этот уровень отвечает за нахождение и запуск тестов, сбор отчетности и логов, интеграцию с внешними инструментами. Он содержит в себе три компонента:
Test Execution Как понятно из названия, этот компонент отвечает за нахождение и запуск тестов. Обычно в него входят две сущности: Test Engine и Test Runner. Test Engine отвечает за нахождение тестов и их исполнение. Test Runner - отвечает за запуск тестов и управление Test Engine(s). Так же он отвечает за интеграцию с IDE, Build и другими инструментами. Получается, что Test Runner может запустить несколько Test Engine согласно плану исполнения и управлять ими. Обычно эти два компонента объединены или как минимум не разделены явно. Но все современные тестовые движки, такие как pytest и JUnit, позволяют запускать тесты на нескольких удаленных машинах. Это позволяет запускать тесты на необходимом для них окружении, например, если тесты платформенно зависимые или есть распределенные тестовые сценарии. Настройте ssh конфиг файл ~/.ssh/config Запуск тестов на нескольких удаленных машинах Ссылка на источник с примером Test Logging Этот модуль отвечает за логирование в тестах. Есть несколько подходов к логированию. Первый - это простой вывод в консоль\файл. Проблема этого подхода в том, что нет уровней детализации, нет форматирования. Также это может быть узким горлышком при многопоточном выполнении, так как в конечном итоге тесты будут бороться за общий ресурс в виде консоли или файла. Следующий подход это использование встроенных или общепризнанных библиотек логирования. Там обычно поддерживаются уровни детализации, форматирование. Правда проблема с доступом к общему ресурсу (консоли или файлу) либо остается, либо частично скомпенсирована использованием буфера или другими приемами. Еще один вариант - это сохранение логов в Log Managment Tool. Например, это могут быть Graylog или Elastic. Тем самым решается проблема доступа к общему ресурсу, так как специализированные сервисы умеют работать в многопоточном режиме. Также логи не будут утеряны, потому что можно настроить backup и разграничить права доступа. К тому же эти сервисы предоставляют моментальный поиск по логам. Test reportingОтчет по тестам. Этот модуль отличается от логирования, так как тут важно видеть не то как прошел тест и причины падения, а какой функционал был протестирован и как. По хорошему, отчет должен помогать как техническим специалистам, содержать логи, причины падения и т.д., так и специалистам со стороны бизнеса, которых интересует что было протестировано, в каком состоянии сейчас продукт и т.д. Для этого, рекомендую интегрироваться с какой-нибудь Test Management system, например, Allure TestOps или дашборд, например, Report Portal. Test Definition Layer (Слой определения тестов)На данном уровне определяются сами тесты и все необходимое для них:
Test Library Тестовые библиотеки - это набор переиспользуемых средств для тестирования. Они предоставляют API к тестируемым функциям. По определению, это очень похоже на адаптеры, но есть различия. Адаптеры находятся на техническом уровне и по сути не зависят от фреймворка. Они дают возможность оперировать с протоколом, с его терминологией. Если это UI, то они дают возможность взаимодействия с элементом интерфейса, например нажать кнопку или найти лейбл. А тестовые библиотеки работают в терминологии уже вашей тестируемой системы. По сути - это шаги теста, например Login, где передается пользователь и пароль, а метод уже сам знает куда нужно перейти и какой флоу нужно пройти, чтобы залогиниться в тестируемом приложении. Функции (шаги) в тестовых библиотеках должны обладать следующими свойствами:
Test ConditionsЕсли переводить с терминологии ISTQB, то test conditions - это Assertions, т.е. обычные ассерты, которые используются в каждом современном тестовом фреймворке. Assertion должен быть:
Ассерты должны показывать ошибку так, чтобы не нужно было повторно прогонять тест локально, а все можно было понять из отчета о падении. Test CasesТест кейсы - это набор конкретных тестируемых требований. Тест кейс содержит в себе:
Когда вначале создается тест кейс в какой-нибудь TMS, то он выглядит довольно структурировано и понятно. Все расписано по шагам, есть ожидаемое поведение и входные данные. Но когда идет перенос тест кейсов в автотесты, очень часто можно встретить вот такую картину, где все намешано и нет никакой структуры. Из-за того, что низкоуровневые вызовы не обернуты адаптерами и логические блоки не объединены в тестовые библиотеки, такой тест как минимум сложно читать, а как максимум невозможно поддерживать в случае изменений протокола взаимодействия с тестируемой системой. Ведь если нужно поменять, например, xpath до какой-нибудь кнопки, то придется проходить по всем тестам, что очень трудозатратно. Все шаги кейса должны быть максимально похожи на описание тест кейса при ручном тестировании. Так автотест становится читабельным, все логические блоки обернуты в шаги. В таком случае, при изменении тестируемой системы, нужно будет поменять логику в одном методе.
Test ProceduresТестовая процедура описывает, какие тесты в каком порядке запускать. Пример свойств, которые можно задать:
В повседневной жизни Test procedures называют как Test suite. Так как управление запуском тестов идет через отдельный внешний модуль, где говорится как и что должно запускаться, следовательно, возникает основное правило при проектировании тестов.
Другими словами, автотесты должны быть независимы друг от друга. Не важно сколько раз и в каком порядке они будут запущены. Поэтому каждый тест должен самостоятельно подготавливать данные или систему перед выполнением и очищать или возвращать систему в исходное состояние после завершения теста. Test DataЭто модуль, который определяет входные параметры для тестовых сценариев. Естественно, что тестовые данные могут приходить в разных форматах и из разных источников, поэтому у тестового фреймворка должны быть необходимые библиотеки и адаптеры к ним для работы с этими входными данными. Классический пример такого модуля - это генератор параметров для REST API тестов на основе Swagger приложения. Так как Swagger содержит описание типов полей API, их ограничения и формат, то можно без труда проанализировать его и на основе этого сгенерировать необходимые параметры для тестов. Test Generation Layer (Слой генерации тестов)Этот уровень определяет “язык” тестов. Для написание тестов можно использовать различные “языки”:
Понятно, что в основе любого тестового фреймворка будет лежать какой-то из языков программирования. Нет проблем продолжать писать автотесты на нем. При правильном проектировании Test Library компонента, тестовых шагов, не будет никакой сложности повысить абстракцию описания тест кейсов, использовав Robotframework или что-то другое, если это необходимо. Так как уже существующие шаги можно будет легко “замапить” на Step Definitions, словесное описание шага, которое используется в Robotframework и подобных фреймворках. Также возможен вариант комбинирования, описание тестов на Robotframework и Python, так как один подход “не закрывает двери” для другого. Можно использовать Robotframework для описания простых тестовых сценариев, для которых он идеально подходит, но при более сложных и нестандартных сценариях, написать тесты с использованием стандартного языка программирования. ИтогТестовый фреймворк - это конструктор, который содержит в себе различные модули. Каждый модуль имеет свою область ответственности. Каждый модуль должен быть максимально изолирован друг от друга, чтобы можно было легко изменить или подменить их в случае необходимости. Если немного преобразовать схему ISTQB, то получается, что только Test Steps могут вызывать и обращаться к адаптерам. Test Cases используют Test Steps и Assertions. Test Steps тоже могут использовать Assertions, так как некоторые общие проверки успешности выполнения должны быть внутри самих шагов, а не “разбросаны” по всем тестам, в противном случае, при изменении шага, придется менять проверку по всему проекту. Test Suites управляют запуском и вызывают Test cases. Test Suites и Test cases используют Test Data, так как на основе нее можно управлять скоупом и поведением тестов, и генерировать тест кейсы динамически. Схема взаимодействия тестовых слоев друг с другом |