Порараз бирацца: как мы учились писать автотесты на Python и что у нас получилось

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Привет, Хабр! Меня зовут Артем Иванюта, в «Магните» я занимаюсь тестированием информационных систем закупок. В статье я расскажу, как наша команда запускала автотесты web-интерфейсов силами одного сотрудника, как мы вписали их в CI/CD-процесс и с чем столкнулись, решая задачу. Кстати, вы наверняка уже догадались, но все-таки скажу — да, я и есть тот самый «один сотрудник». Так что никакого кликбейта.

Одиннадцать друзей Иванюты

В нашей команде 11 человек, мы отвечаем за тестирование 15 информационных систем. Всего в «Магните» их больше 600. Мы занимаемся тестированием web-инструментов цепочки поставок розничной сети. Это, например:

  1. система автоматизированных рабочих мест сотрудников;

  2. SRM-система снабжения сырьем собственных производств и закупочной логистики;

  3. электронный документооборот EDI;

  4. информационная система графика поставок товара в магазины,

  5. система управления ареалами AMS. 

В масштабах «Магнита» это 25 000 пользователей — наших сотрудников, 4 500 — контрагентов и 16 000 торговых точек. Мы производим релизы ежедневно, а сам цикл в среднем составляет от 2 до 4 недель. По сути от нас зависит своевременная поставка товаров тысяч поставщиков на полки 16 тысяч магазинов (и много чего еще).

Восстание машин: срываем релизы

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

В 2018 году отдел тестирования запустил внутреннюю школу тестировщиков. За месяц я освоил базу и навыки, а затем перешел в команду тестирования web-интерфейсов. Пришел я как раз вовремя: поток входящих задач начал стремительно расти. Компания пошла по пути DevOps. Запускались новые системы, серьезно обновлялись основные.

К началу 2019 года объем тестирования увеличился вдвое. Так количество тикетов на каждого возросло в среднем с 30 до 70 в месяц, а горизонт планирования релизов сдвинулся на 2 месяца вперед. 

При этом количество людей в команде оставалось прежним — все те же 11 друзей Иванюты:)

Когда графики релизов начали срываться на регулярной основе, а рутинные операции занимать больше 50% времени, мы поняли, что дальше так не потянем. Тогда и было принято решение часть сценариев покрыть автотестами. 

Путь в питонисты

До 2019 года никто из нашей команды не занимался автотестами, 100% тестов обрабатывались вручную. Никто из нашей команды не умел кодить. И конечно мы не могли снижать темп основных задач. Поэтому всей командой уйти в обучение автоматизации тестирования тоже было нельзя. Решили, что в разведку пойдет кто-то один. Мне было интересно попробовать: я изучил опыт сообщества и остановился на python. Python считается универсальным языком, поскольку подходит под множество задач. К примеру, на Python написан Instagram, его используют в аналитике данных, запуске космических кораблей и... в автоматизации тестирования. Чтобы скорость работы команды не снижалась, приняли совместное решение — я иду обучаться, команда забирает 80% моих задач. 

Какие цели мы поставили для внедрения автотестов:

  1. Увеличение скорости тестирования.

    Мы накопили довольно большую библиотеку регрессионных сценариев тестирования различных веб-интерфейсов. Релизы в команде проводятся каждый день, поэтому регресс востребован и выполняется тестировщиками регулярно вручную. Его выполнение занимало не меньше 8 часов, а автоматизированные тесты могли сократить время на обработку регресса до 95% и больше. 

  2. Повышение качества тестирования.

    При частом прогоне рутинных проверок всегда есть риск пропустить ошибку. Машина же совершит её с меньшей вероятностью за счёт многократного прогона автотестов. Разработчик самостоятельно может запустить автотестирование после изменения кода без помощи тестировщика. То есть это время команды освобождается для других задач.

  3. Рост экспертизы.

    Разработанные автотесты нужно сопровождать и развивать. Мы решили сделать ставку на развитии своей команды и запустили "Школу автотестирования".  Я на себе прочувствовал, как «заряжает» обучение на реальных задачах. В Магнит я прошел такой опыт дважды, получив и новую экспертизу, и развитие. Программу Школы выстроили по такому же принципу — много практики и реального опыта. 

  4. Поддержка DevOps-стратегии компании.

    В 2018 году «Магнит» взял курс на развитие DevOps. Наши 15 систем не стали исключением. Их ждали обновление и переход на новые практики и инструменты. Автотесты — одна из обязательных стадий процесса CI/CD. 

Вот так архитектурно он выстроен у нас:

Так мы договорились о целях и тактике. Следующая задача — понять, что именно покрывать автоматизацией.

Автоматизируем регресс

Мы обновляем информационные системы «Магнита» под условия бизнеса практически ежедневно:

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

Во-вторых, пользователи от бизнеса направляют запросы на новый функционал. В среднем на сотрудника приходится по 2–3 системы в день. 

Получается, попытка автоматизировать все кейсы сразу была бы обречена на провал: сил могло хватить только на актуализацию тестовых сценариев. 

После анализа всей базы тестовых случаев для автоматизации выбрали регрессионные сценарии. 

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

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

В TMS системе TestRail создал 3 группы приоритета регрессионных сценариев для последующей автоматизации:

  1. Критичный — например, кейсы направлены на проверку доступности всех вкладок, кнопок, фундаментальной логики приложения. Ошибки в этой группе тестов могут привести к полной блокировке работы инструмента;

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

  3. Низкий — кейсы не влияют на бизнес-процессы, но должны выполняться при каждом регрессе. Например, поиск, фильтрация и другие.

Теперь расскажу про пошаговую настройку процесса и инструментов.

Запускаем змея на Pytest

Я использовал Python и его фреймворк Pytest. Оба инструмента широко используются в мире тестирования, по ним накоплена база знаний, есть много плагинов. Поэтому вместе с Selenium WebDriver такой набор удовлетворяет всем потребностям в тестировании web-интерфейсов.

Для работы с базой данных я использовал библиотеку SQLAlchemy. Это одна из самых популярных библиотек для работы с СУБД для Python. 

При создании структуры проекта брал паттерн Page Object. Этот шаблон проектирования позволяет разделить код тестов и описание страниц. Так тесты приобретают читаемый вид, а методы работы со страницей мы можем переиспользовать. Результат: упрощаем поддержку кода и уменьшаем его количество.

Структура проекта выглядит так:

Папки:

  1. Configuration – для настройки подключения к БД, учетные данные для авторизации в приложении;

  2. Locators – хранит локаторы для поиска элементов на странице;

  3. Pages – содержит методы для работы со страницами. Для каждой вкладки приложения используется своя страница с методами работы с ней;

  4. Tests – хранит сами тесты;

  5. Tools – хранит инструменты для подключения к БД. Это создание самого подключения, методы работы с БД, запросы к БД.

Файлы:

  1. conftest.py — хранение фикстур. Фикстуры в контексте pytest — это вспомогательные функции для наших тестов, которые не являются частью тестового сценария;

  2. pytest.ini – для регистрации меток маркировки тестов;

  3. requirements.txt – файл зависимостей приложения для автоматической установки пакетов python с помощью утилиты pip;

  4. testrail.cfg – конфигурационный файл с настройками для TestRail.

Помещаем в base_page методы или локаторы, которые используются в кейсах для разных страниц:

Как в итоге выглядит тест? Лаконично :)

@pytestrail.case('C128952')
 @pytest.mark.order_gm
 def test_btn_filter(self):
     self.orders_gm.click_on_btn_filter()
     self.orders_gm.should_be_open_filter_form()
     self.orders_gm.click_on_whs()
     self.orders_gm.input_name_gm_in_filter_form()
     self.orders_gm.click_on_search_trigger()
     self.orders_gm.click_on_x_tree_selected()
     self.orders_gm.click_on_whs()
     self.orders_gm.input_docdate_from()
     self.orders_gm.input_docdate_to()
     self.orders_gm.click_on_btn_filter_2()
     self.orders_gm.should_be_row_in_grid_table_3()

В  pages мы расписываем сами методы. Клик на кнопку:

def click_on_btn_filter(self):
     btn = self.browser.find_element(*BasePageLocators.BUTTON_FILTER)
     btn.click()

Локаторы отдельно:

BUTTON_FILTER = (By.XPATH, '//button[text() = "Фильтр"]')

Для поиска элементов на странице я использовал язык запросов XPath. Часть элементов страницы имеют уникальные name или id, которые указали разработчики фронта, поэтому к ним легко обращаться:

CONTR_ID = (By.XPATH, '//*[@id = "contr_id_view"]')

А другая часть не имеет таких уникальных идентификаторов, поэтому здесь помогают возможности языка XPath:

X_COMBO_SELECTED_LAST = (By.XPATH, '(//*[@class = "x-combo-list-item x-combo-selected"])[last()]')

Запуск и закрытие браузера вынесены в conftest.py. 

Добавляем функцию, например так:

def pytest_addoption(parser):
     parser.addoption('--browser_name', action='store', default="chrome",
                      help="Choose browser")

И фикстуру:

@pytest.fixture(scope="function")
 def browser(request):
     browser_name = request.config.getoption("browser_name")
     if browser_name == "chrome":
         capabilities = {
             "acceptInsecureCerts": True,
             "browserName": "chrome",
             "version": "73.0",
             "enableVNC": True,
             "enableVideo": False,
             "name": "Interface name"
         }
 
        browser = webdriver.Remote(command_executor="http://10.8.153.230:4444/wd/hub",
                                    desired_capabilities=capabilities)
         browser.maximize_window()
 
    else:
         raise pytest.UsageError("--browser_name should be chrome or firefox")
 
    yield browser
     browser.quit()

В самом тесте мы не расписываем авторизацию и переход на нужную вкладку, это все выносится в setup с помощью фикстуры: 

@pytest.fixture(scope="function", autouse=True) 
 def setup(self, browser):
    # Переход к странице авторизации  
    login_page = LoginPage(browser)  
    login_page.open() 
    login_page.authentication()  
    # Переход на вкладку  
    login_page.open_tab_orders_gm()
    self.orders_gm = OrdersGMPage(browser, browser.current_url) 

В параметрах фикстуры уже можно указать, когда его использовать. В данном случае наш setup запускается перед каждой функцией (тестом) с помощью параметра scope="function".

Ключ на старт: запускаемся через Gitlab CI

Для каждого проекта есть свой job в Gitlab CI, который запускается при необходимости. Последняя версия проекта тянется с репозитория. 

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

Развернув интерфейс тестирования можно увидеть, что происходит в режиме реального времени:

Selenoid создаёт для каждого теста чистый браузер, в котором выполняется автотест. После его завершения происходит удаление данных с помощью Python. Тесты не зависят друг от друга и могут запускаться в несколько потоков. Тестировать приложение можно одновременно с нескольких браузеров. Для запуска тестов в потоках используется pytest-xdist плагин. В команду запуска добавляется параметр -n <количество потоков>.

Фреймворк Pytest позволяет выбрать часть кейсов или пропускать те, которые сейчас не нужны. Например, для кейсов по одной вкладке приложения ставим маркировку @pytest.mark и запускаем тестирование нужной вкладки приложения. Фикстуры позволяют запустить тестирование в нужной версии браузера.

Получаем отчет в TestRail

Основным инструментом получения отчетности служит TestRail. Каждый автотест связан с кейсом в TestRail. Для связи использую глобальный идентификатор кейса через плагин pytest_testrail. Настройка выполняется в конфигурации testrail.cfg на основе документации. В начале кейса нужно добавить строчку вида @pytestrail.case('C128952').

Так выглядит результат выполнения автотестов, переданный в TestRail: 

В названии передаю id джобы и название интерфейса, который тестирую. Вижу полную информацию по тестам, которые прошли успешно:

и детальную информацию по ошибкам, если они есть:

Как интегрировать автотесты в CI-процесс

Файлы с автотестами добавляю в репозиторий, где ведется разработка самого приложения. При пуше видим последнюю версию тестов. Далее добавляю stage: autotest в файл конфига сборки .gitlab-ci.yml. Команда для запуска может выглядеть например так:

- pytest -v ./test/ams_selenium_autotest/ --testrail --tr-testrun-name={$CI_PIPELINE_ID}_AMS_2.0_Automated_Run

Переменная CI_PIPELINE_ID добавлена для идентификации сборки в TestRail.

После пуша в ветку запускается сборка по алгоритму. Описание храним в файле gitlab-ci.yml. Если стадия автотестов пройдёт успешно, то актуальная версия выкатит в продакшн.

Автотесты во спасение или Выход из штопора

План сработал, мы смогли запустить автоматизацию и разгрузить команду: 

  1. Среднее время выполнения регрессионного сценария сократилось на ~95% и стало занимать 20 минут;

  2. Сократилось время выпуска релизов (Time To Market), а их количество в месяц выросло с 4 до 6;

  3. Добились отказоустойчивости программного продукта при поставках, исключили риски в регрессе;

  4. Из 1000 тестовых кейсов повысили количество покрытых автотестами до 400.

Этот год стал челленджем для всей команды и для меня лично. 

Мы сделали выбор в пользу TestRail из-за его интеграции с основным инструментом тестирования Pytest. Плюсом стало наличие готовых фреймворков. Gitlab CI используется в разработке информационных систем коммерции и полностью соответствует нашему запросу. Поэтому предпочли тому же Jenkins.

По хорошей традиции запустили школу «Автоматизации тестирования» на практике, наш опыт может быть полезным другим командам тестирования в «Магните». Первые студенты-тестеры завершили обучение. Так практические навыки распространяются в компании. 

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

С какими проблемами перехода на автоматизацию тестирования столкнулись вы? 

Как решали сложные кейсы автоматизации функционального тестирования?

Источник: https://habr.com/ru/company/magnit/blog/582992/


Интересные статьи

Интересные статьи

16 июня в 19.00 (Мск) Яндекс.Практикум проводит открытый вебинар «Как начать писать о технологиях». На вебинаре мы расскажем, что такое хороший технический текст и с помощью каких приёмов...
В MS Outlook есть прекрасный раздел задач. Это удобно и быстро. Можно с утра (или с вечера) накидать себе задачек на день и постепенно расщёлкивать их. Вторым уровнем гру...
Привет, Хаброжители! Взрывной интерес к нейронным сетям и искусственному интеллекту затронул уже все области жизни, и понимание принципов глубокого обучения необходимо каждому разработчи...
Мы просмотрели и сравнили 10 000 open source библиотек для Python и выбрали 34 самые полезные. Мы сгруппировали эти библиотеки в 8 категорий.
«Пять экзабайт информации создано человечеством с момента зарождения цивилизации до 2003 года, но столько же сейчас создаётся каждые два дня». Эрик Шмидт Datatable — это Python-библиотека дл...