Статья является продолжением ранее опубликованной на Habr статьи Еще одна инструкция о том, как с нуля начать писать UI автотесты на Python + Pytest + Playwright + QASE.io.  Как и первая статья эта будет написана под MacOS и на python версии 3.11.1. В качестве IDE выступает PyCharm Community Edition. Весь код из данной статьи будет доступен в репозитории на GitHub.  Шаги в тест-кейсах на QASE.io создаются и актуализируются автоматически  В первой части мы создали аккаунт в GitHub и научились создавать репозиторий. Теперь нам понадобится еще один. Я назову его article-two.  Пока он пустой. Нам необходимо его склонировать на рабочий компьютер. Ранее я говорил, что все склонированные репозитории я храню в директории Code. Так как статей теперь две, то я создал дополнительную директорию articles. Именно туда я и буду клонировать только что созданный репозиторий.  В моем случае команда на клонирование выглядит следующим образом:  Первая команда позволяет мне перейти из директории пользователя в директорию articles. Вторая команда клонирует репозиторий на мой компьютер.  Теперь мы можем приступать к созданию нового проекта в PyCharm. Создавать новый проект в PyCharm мы учились в первой статье. В этот раз все сделаем аналогично.  Теперь нам необходимо скопировать файлы, которые мы создавали в рамках первой статьи и положить их в папку article_two, то есть в папку текущего проекта.  Для этого я просто захожу в Finder, выделяю файлы в папке article_one, нажимаю Command + C, открываю папку article_two и нажимаю Command + V.  Но есть важный нюанс. В папке article_one есть скрытые директории и файлы. Видеть их в Finder мы уже умеем (надо нажать Command + .). Нам важно не копировать директории .git, .idea и  venv. Директория .git хранит данные о репозитории, с которого была склонирована папка. В папке article_two у нас такая папка уже есть. Потому что там другой репозиторий. Аналогичная история с папкой venv. Там хранятся данные по виртуальному окружению проекта. И так как проекты разные, то у каждого из них будет своя уникальная папка venv. Поэтому копируем все, кроме этих трех директорий.  Теперь в новом проекте в PyCharm есть все файлы из предыдущего проекта.  Важно отметить, что теперь нам необходимо добавить эти файлы в GitHub и затем установить все зависимости из requirements.txt. Делать это мы учились в первой статье.  Остается убедиться, что мы ничего не поломали в процессе переноса файлов. Для этого в терминале PyCharm запустим команду pytest .  В первой статье мы создали аккаунт в QASE.io. Там же создали первый свит и первый кейс.  Откроем кейс.  Пока мы заполнили только наименование кейса. И сделали мы это сами, вручную. Теперь мы можем создать шаги внутри этого кейса. Повторим те шаги, которые уже реализовали в автотесте.  Сохраним изменения в кейсе.  Все операции по редактированию кейса, внесению в него шагов мы делали вручную. Это не было сложно. Но, когда кейсов в документации становится много (300-500 штук, а часто и 1000+) работа по написанию шагов в каждом кейсе становится большой работой. Да, это не так уж сложно. Но рабочее время тратится.  Кроме того, помимо создания новых кейсов, нам надо помнить об уже имеющихся. То есть рабочее время будет тратиться не только на создание новой документации, но и на актуализацию шагов в уже ранее написанных кейсах.  Практика показывает, что в день лично я готов писать документацию по 20 новым тест-кейсам. Или столько же актуализировать. Поэтому когда я беру задачу по актуализации 300 кейсов, то понимаю, что на это уйдет вся моя рабочая неделя.  Мне процесс актуализации документации вручную нравится мало, поэтому в рамках этой статьи мы попробуем автоматизировать процесс наполнения/актуализации тестовой документации шагами.  Чтобы было понятнее, что будет происходить в дальнейших шагах я бы хотел попробовать описать то решение, к которому хотел бы прийти в рамках этой статьи.  Наша цель: автоматически обновлять шаги в тест-кейсах QASE.  Чтобы достичь цели мы должны создать фикстуру со скоупом function. То есть фикстуру, которая будет "что-то" делать перед запуском автотеста и "что-то" делать после завершения автотеста.  До запуска автотеста фикстура будет создавать json файл, где будут собираться все шаги текущего автотеста.  Чтобы собрать информацию о каждом шаге в json файле напишем декоратор и обернем в него каждый шаг автотестов. Декоратор будет запускаться перед каждым шагом автотеста и добавлять в json файл информацию о шаге. Таким образом пока будет прогоняться автотест мы будем собирать информацию о каждом шаге автотеста в json файле.  После завершения автотеста продолжит выполняться фикстура. Она переработает собранную в json файле информацию в такой вид, чтобы она была понятна API QASE. А следующим шагом фикстура инициирует запрос к API QASE, который и обновит шаги в кейсе в QASE.io.  Звучит, кажется, сложно. Но если смотреть на итоговый код, который мы получим к завершению статьи, то там все вполне прозрачно.  Интеграцию с QASE.io мы будем делать через API. Всю информацию будем получать из официального сайта с документацией по API QASE.  Чтобы актуализировать шаги в кейсе, потребуется метод Update test case, вот ссылка на страницу с описанием.  Что нам потребуется, чтобы воспользоваться этим методом?  Token  Код проекта ID кейса Чтобы получить Token наводим мышку на свою аватарку в правом верхнем углу сайта QASE.io. Откроется меню. В нем выбираем пункт API Tokens.  На открывшейся странице нажимаем кнопку Create new API token. Вводим наименование. Можете в качестве наименования использовать имя репозитория в GitHub, которое вы используете для работы над этой статьей. Затем остается нажать кнопку Create.  Откроется окно с токеном. Скопируйте его и сохраните в надежном месте. Я сохраню в 1Password, чтобы всегда иметь к нему доступ.  Теперь о Коде проекта. Чтобы узнать код проекта, достаточно открыть страницу самого проекта.  И остается найти ID кейса. Мы говорили об этом в первой статье. Напомню, что найти его можно практически на любой странице QASE.io Итого. Мы нашли Token, код проекта и ID кейса.  Чтобы убедиться в том, что все подготовленные нами данные корректны, мы воспользуемся страницей с методом Update test case.  Открываем ее и находим интересующие нас поля. Заполняем их.  После заполнения полей нам остается найти поле Steps. В нем нажимаем на кнопку Add object. В открывшейся форме нас интересуют три поля. Action, Data, Expected_result. Эти поля мы сейчас заполним.  В качестве значений будем использовать сами названия полей. Нам эта операция нужна лишь для того, чтобы убедиться, что полученные нами данные корректны.  В поле action пишем Action. В поле expected_result пишем Expected result. В поле data пишем Data. Мы заполнили все необходимые поля. Остается нажать кнопку Try it в правой части сайта. Нажатие кнопки приведет к тому, что запрос будет отправлен и данные в нашем хранилище кейсов будут изменены. Нажимаем Try it.  Смотрим блок Response. Так как нам вернулся Status: True, то можно считать запрос успешным. Теперь остается зайти в сам кейс и убедиться, что данные в шагах были изменены.  Как видим, данные в кейсе обновились в соответствии с теми, которые мы подготовили перед нажатием кнопки Try it.  Мы убедились, что у нас есть все необходимое, чтобы возвращаться к коду  автотестов и уже там настраивать запросы к API, которые будут обновлять документацию в соответствии с шагами автотестов.  Для того, чтобы присвоить каждому автотесту ID из QASE воспользуемся возможностями pytest. А именно '@pytest.mark.___'. Полностью код будет выглядеть следующим образом:  Нас интересует строка номер 7. В ней мы используем декоратор pytest.mark, с аттрибутом case_id (наименование атрибута мы придумываем сами, можно использовать любой). В виде параметра передаем ID кейса из QASE. Теперь, в процессе выполнения автотеста, мы можем использовать его ID. То есть сможем отправить запрос к API QASЕ на обновление шагов кейса. Для того, чтобы отправить запрос в QASE на изменение шагов кейса, нам надо эти шаги собрать в процессе работы автотеста. Поэтому первым делом мы добавим к каждому шагу автотестов информацию о том, что они делают. Ровно также, как мы заполняли шаги в QASE. То есть нам надо для каждого шага автотестов подготовить action, data и expected_result. Шаги автотестов хранятся в директории pages. Там есть модуль index_page.py. Откроем его. В нем три шага.  Что делает первый шаг? Открывает Index page. В качестве data можно указать https://google.com. То есть адрес Index page. А expected_result представляет из себя успешно открывшуюся страницу.  Как добавить эту информацию к шагу?  Вносим правки в модуль index_page.py. На третьей стори импортировали qase. Пока PyCharm ругается на ошибку, что такого модуля нет. И это так. Мы его создадим совсем скоро.  В строке 10 мы указали на декоратор, который скоро опишем в модуле qase. Строки 11, 12, 13 - это параметры декоратора, которые содержат в себе те самые сведения о шаге, которые требуются QASE.  Аналогичным образом добавим сведения для всех остальных шагов.  Для всех трех имеющихся шагов мы добавили информацию, которая далее будет передаваться в QASE. Как можно заметить, у второго и третьего шага не указан параметр data. Только параметр action является обязательным. Остальные опциональные.  Теперь, когда мы прописали информацию о шагах, надо написать сам декоратор, который эту информацию будет обрабатывать.  Первым делом в корневой директории проекта (у меня это директория article_two) добавим новую директорию qase. В ней будем собирать все модули, которые потребуются для интеграции автотестов с QASE.io.  В директории qase создадим новый модуль docs.py.  В модуле docs.py создадим декоратор step, который мы уже добавили ко всем шагам автотеста.  Сейчас PyCharm ругается на строку 5. Потому что класс Docs мы еще не написали. Но с тем, как работает декоратор и класс Docs, мы разберемся чуть позже. А пока нам необходимо в директории qase создать модуль '__init__.py'. И в нем импортировать созданный декоратор.  Открываем модуль index_page и проверяем, что PyCharm перестал ругаться на отсутствие модуля qase. Ошибки больше нет.  Но, как мы помним, у нас есть ошибка в модуле docs.py. Там мы используем класс Docs, который еще не создали. Займемся этой задачей.  Класс Docs призван обеспечить корректную работу декоратора step.  Поэтому класс Docs будет решать несколько задач:  Создать временный файл json, куда мы складывать все шаги из запущенного автотеста.  Удалять этот файл, когда исполнение автотеста завершено, а данные отправлены в QASE.  Здесь же будет метод, который будет считывать значения параметров декоратора (action, data, expected_result) и сохранять эти значения во временный файл.  Также в классе Docs разместим вспомогательные методы, которые позволят читать файл json и сохранять изменения в нем.  Целиком модуль docs.py вместе с декоратором step и классом Docs будет выглядеть следующим образом:  После того, как мы добавили класс Docs проверяем, что PyCharm перестал ругаться на ошибку отсутствия класса Docs в декораторе step. Да, ошибки больше нет.  Методы по созданию и удалению временного файла json, которые мы реализовали в классе Docs, нам необходимо будет использовать в фикстуре pytest, которая будет отвечать за сбор, подготовку и отправку данных в QASE. Для удобного использования этих методов в других модулях нашего проекта вернемся в модуль '__init__.py' в директории qase. Импортируем и инициализируем класс Docs. Предлагаю добавить изменения в GitHub и переходить к работе над первой фикстурой.  Первым делом в директории fixtures создадим новый модуль qase_fixtures.py.  В модуль qase_fixtures.py добавляем следующий код: Важно отметить, что после вставки кода в модуль PyCharm покажет ошибки о том, что мы еще не написали модули config.qase и qase.api. Так и должно быть. Мы их скоро напишем. А пока посмотрим на то, что делает код фикстуры.  Строка 8. Обозначаем, что это фикстура. И используем параметр autouse. Он может принимать значения True/False. Так как нам не нужно будет при каждом запуске pytest генерить документацию, то мы настроим config.qase так, чтобы он мог принимать из команды в терминале значение переменной IS_DOC_UPDATE_NEEDED. Таким образом, задавая в терминале значение этой переменной как True или False мы сможем управлять поведением документации (генерим ее или нет).  Строка 9. В качестве параметра функции передаем request. Он позволит нам получать доступ к значениям в атрибуте '@pytest.marl.case_id(1)', который мы добавляем к каждому автотесту.  Строка 10. Получение значения параметра из аттрибута case_id. Строка 11 и 14. Для отправки запроса в API QASE потребуется Token, который мы ранее получили на сайте QASE.io. Но Token надо хранить в безопасности. Хранить его в коде проекта мы не будем. Поэтому Token будет работать аналогично с переменной IS_DOC_UPDATE_NEEDED и его значение мы будем получать из команды в терминале. На следующем шаге мы создадим модуль config.qase, где опишем поведение всех этих переменных. Строка 12. Используем метод create_database, который создает файл json для хранения информации о шагах. Строка 13. Сам по себе yield несет в себе два важных момента. Во-первых, он может работать как return, если после yield указать что-либо. Во-вторых он является разделителем. То, что написано до yield будет выполнено перед автотестом. То, что написано после yield будет выполнено после автотеста. Поэтому перед yield у нас идет создание файла, а после него отправка значений в QASE.io и удаление файла.  Строка 15. Запрос к API QASE. В этом запросе мы обновим шаги в кейсе. Запрос к API QASE создадим совсем скоро. Чтобы в будущем pytest мог найти эту фикстуру нам следует добавить ее в файл conftest.py:  Таким образом сейчас необходимо создать config.qase и api.qase_api. Займемся этим. В директории config создаем новый модуль qase.py. В созданный модуль добавляем следующий код:  Строка 5. В модуле qase_fixtures.py мы использовали конструкцию if config.qase.TOKEN, которая либо позволит выполниться коду после нее, если значение config.qase.TOKEN не будет равно False. Так вот в строке 5 модуля qase.py мы считываем значение переменной QASE_TOKEN из команды в терминале и если она не равна None (то есть если такая переменная была передана в команде), то сохраняем в эту переменную токен. Если же такой переменной не было в команде в терминале, то присваиваем TOKEN значение False, что не позволит выполняться коду, связанному с интеграцией с QASE.io.  Строка 6. Работает по аналогии со строкой 5.  Строки 8, 9, 10 хранят в себе значения, необходимые для отправки запроса к API QASE. Эти данные нам совсем скоро пригодятся.  Строка 11 хранит код проекта из QASE, который нужен для отправки запросов к API QASE.  Не забудем импортировать и инициализировать модуль qase.py в модуле '__init__.py' директории config.  Сохраним изменения в GitHub и переходим к задаче с отправкой запроса к API QASE. В корневой директории проекта создадим директорию api. В ней мы будем собирать все модули, которые связаны с API. Первым таким модулем станет qase_api.py. Создадим модуль.  В модуль qase_api.py добавим код, в котором создадим класс и определим необходимые переменные.  Строка 6. Заголовки потребуются для отправки запроса к API QASE.  Строка 11 и 12. Значения переменных DOMAIN и PROJECT будут необходимы для составления URL в запросе к API.  Теперь добавим сюда сам метод запроса к API QASE и проверку ответа.  Итого полностью модуль qase_api.py выглядит следующим образом: Теперь остается создать модуль '__init__.py'  в директории api и добавить в него следующий код, который импортирует и инициализирует класс QaseAPI: Остается зайти в модуль qase_fixtures.py и убедиться, что там нет ошибок. Все наработки зальем в GitHub.  Если смотреть на ТЗ, то мы сделали все, что планировали. Но сейчас надо убедиться, что все работает корректно.  Ранее мы запускали автотесты командой pytest . Сейчас перед самой командой нам надо добавить переменные, от которых зависит запуск интеграции с QASE. Нам необходимо добавить следующие переменные:  QASE_DOC=True QASE_TOKEN=Значение токена, который получен из QASE.io В целом команда должна выглядеть следующим образом:  После запуска команды видим, что автотест прошел успешно. Но появилось сообщение о том, что используется не зарегистрированная маркировка case_id.  Чтобы исправить этот момент достаточно в корневой директории добавить файл pytest.ini со следующим кодом:  Таким образом мы можем сообщить pytest, что такая маркировка нами предусмотрена.  После перезапуска тестов получаем результат уже без сообщений о маркировке:  Чтобы увидеть, какие фикстуры в каком порядке выполнялись при прогоне, можно в конец команды добавить --setup-show.  Команда получится такой:  А результат выполнения команды теперь содержит наименования фикстур: Остается проверить, что шаги в QASE обновились. Для этого открываем кейс:  Теперь, независимо от того, 1 у нас кейс в документации или 3000, шаги в кейсах будут актуальными и тратить дополнительное время на актуализацию документации не нужно.   QA Automation Engineer в YourCoach.Health