24 августа 2018 Время чтения: 6 минут

Магнитофон — инструмент для записи автотестов

Виктор Буров

Виктор Буров

Разработчик

ISPSystem
Добрый день, уважаемые читатели. Меня зовут Виктор Буров. Я работаю разработчиком в компании ISPsystem и хочу поделиться опытом автоматизации тестирования.
Так сложилось, что у нас превалировало ручное тестирование, и тестировщики тратили кучу времени на выполнение одних и тех же действий. Однажды мы подумали: почему бы не научить панель повторять действия тестировщика, ведь, по сути, все они превращаются в конкретные вызовы API. Это бы позволило писать тесты людям даже без навыков программирования.
Мы решили написать модуль создания автоматических тестов. Чтобы тестировщик мог просто нажать кнопку создания теста, выполнить условия тест-кейса и по окончании нажать «завершить» — и всё, тест был готов! Простая идея, но реализовать ее оказалось непросто. Потому что мы хотели, чтобы этот модуль был максимально адаптирован под наши продукты и использовал преимущество унифицированного интерфейса: чтобы сделанная запись выглядела как готовый тест-кейс. Это бы полностью избавило от ручной работы по написанию тестов. Получившаяся в итоге система получила название «магнитофон».

Принцип работы

Все параметры запросов (HTTP-заголовки, переменные окружения, POST-данные, если такие есть) и весь ответ записывается в xml-файл. Каждой записи присваивается порядковый номер. Все запросы разделяются на модифицирующие и не модифицирующие. После записи теста многие из немодифицирующих запросов вырезаются, так как не влияют на выполнение тестов и только затягивают и запутывают процесс выполнения (отсюда пропущенные порядковые номера на скриншоте).
Во время записи магнитофон позволяет в один клик устанавливать проверки значений в полях на формах и в колонках списков. Также позволяет записывать негативные тесты, запоминая, какую ошибку вернула панель во время записи теста.
При воспроизведении запросы отправляются напрямую в API приложения, не используя браузер.
По сути магнитофон — это модуль, который встраивается во все наши продукты и позволяет устанавливать реагирующие на события обработчики. Написан при помощи COREmanager.
Пример записи одного вызова, сделанного магнитофоном:

  
        210
        application%2Fx%2Dwww%2Dform%2Durlencoded%3B%20charset%3DUTF%2D8
        on
        text%2Fhtml%2C%20%2A%2F%2A%3B%20q%3D0%2E01
        en%2DUS%2Cen%3Bq%3D0%2E5
        no%2Dcache
        keep%2Dalive
        corelang5%3Dorion%3Aru%3B%20ispmgrlang5%3Dorion%3Aru%3B%20ipmgrlang5%3Dorion%3Aru%3B%20ipmgrses5%3Dbdd69179d627%3B%20ispmgrses5%3D14157f7bbc5e%3B%20menupane%3D30%5Faccount%2D1%253A30%5Fdomains%2D1%253A30%5Fwebserver%2D1%253A30%5Fantispam%2D1%253A30%5Fmaintain%2D1%253A30%5Ftool%2D1%253A30%5Fstat%2D1%253A30%5Fsrvset%2D1%253A30%5Fsysstat%2D1%253A30%5Fintegration%2D1%253A30%5Fset%2D1%253A30%5Fmgrhelp%2D1
        172%2E31%2E240%2E175%3A1500
        Web%2Dinterface
        no%2Dcache
        https%3A%2F%2F172%2E31%2E240%2E175%3A1500%2Fispmgr
        Mozilla%2F5%2E0%20%28X11%3B%20Ubuntu%3B%20Linux%20x86%5F64%3B%20rv%3A24%2E0%29%20Gecko%2F20100101%20Firefox%2F24%2E0
        XMLHttpRequest
        
        
        38640
        POST
        %2Fispmgr
        %2Fispmgr
        172%2E31%2E240%2E175
        172%2E31%2E240%2E175
        1500
      
  func%3Demaildomain%2Eedit%26elid%3D%26name%3Dtest%2Eemail%26owner%3Dusr%26ipsrc%3Dauto%26defaction%3Derror%26redirval%3D%26spamassassin%3Doff%26avcheck%3Doff%26clicked%5Fbutton%3Dok%26progressid%3Dfalse%5F1424243906672%26sok%3Dok%26sfrom%3Dajax%26operafake%3D1424243906673
      
        
          
            
// Поля формы

Доработки (о чем мы не подумали заранее)

Ожидание

Человек может «просто подождать», компьютер — нет. Одна из первых проблем, которую должен был решить магнитофон, — написание вейтеров. Чего только народ не придумывал, чтобы дождаться завершения операции. Было реализовано ожидание фоновых задач и возможность добавить остановку на указанном шаге на заданное количество секунд.

Дозапись и редактирование шагов теста

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

Макросы для переменных

При выполнении тестов значения в передаваемых и проверяемых параметрах менялись в зависимости от сервера, на котором они были запущены. Пример таких данных — IP-адреса. Узнать их на этапе записи теста невозможно, поэтому я добавил систему макросов. Это позволило создавать тесты, не так жёстко привязанные к окружению. Недостаток решения в том, что после записи макросы надо указывать вручную.
Ещё одна проблема, существенно усложняющая работу с магнитофоном, — это использование не нативных ключей. Мы заметили её не сразу, так как тестировали магнитофон на ISPmanager, который использует нативные идентификаторы. Но в некоторых других панелях запись идентифицируется по уникальному ID. Поэтому пришлось научить магнитофон не только получать идентификатор после создания записи или объекта (так как ID может меняться от запуска к запуску), но и подставлять его во все последующие запросы.

Поддержка формата JUnit

Созданные магнитофоном тесты запускаются автоматически в среде непрерывной интеграции Jenkins. После выполнения тестов создаётся xml-файл, содержащий данные в формате JUnit. Чтобы файл корректно формировался, было введено ограничение на именование тестов. Например, тест User.Create.xml попадал в testsuite с именем User и, следовательно, testcase у него был Create. В случае ошибки к нему добавлялся дочерний узел failure с полным описанием ошибки.

Метрики

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

Хранилище тестов

Хранилище тестов в первую очередь решает проблему переноса готовых тестов на другие серверы. Ещё оно пригождается, когда тесты пишут несколько тестировщиков. Небольшая панель для хранения тестов Storage была разработана и развёрнута также на базе нашего COREmanager. В магнитофоне есть модуль синхронизации тестов с хранилищем. При написании нового теста или выгрузке тестов из хранилища они автоматически становятся недоступными к загрузке в хранилище, чтобы не возникло путаницы. После изменения теста у него повышается номер ревизии, чтобы в хранилище загружались только тесты после изменений.

Трудности (ну а куда же без них)

Использование магнитофона показало, что не все функции API следовали нашим же внутренним рекомендациям. В частности, не все функции возвращали идентификатор записи после её создания. Приходилось возвращаться в том числе к работающему коду и приводить его в соответствие с требованиями.
Ещё одной проблемой стали deadlock. Панель подразумевает выполнение некоторых критических действий в монопольном режиме. И магнитофон, являясь частью той же панели, вызывая такие функции приводил к зависанию всей системы. Это удалось определить только с помощью GDB (никто не вспомнил об этой особенности). К сожалению, не обошлось без костылей, потому что было принято решение при запуске тестов магнитофона выполнять эти функции в многопоточном режиме. Теоретически можно было оформить магнитофон не модулем, а отдельной панелью. Но мы не пробовали.
Написать и забыть тоже, к сожалению, не получилось. Интерфейс наших продуктов меняется и усложняется. Причём количество компонентов интерфейса активно растет. Поэтому магнитофон приходится время от времени дорабатывать, чтобы при появлении новых компонентов он мог анализировать их структуру и обрабатывать результаты выполнения запросов.

Заключение

Создание магнитофона помогло добиться повышения качества тестируемых продуктов. Сэкономлено время и ресурсы на обучение тестировщиков. Попутно магнитофон позволил провести review нашего API на соответствие внутренним рекомендациям, и, следовательно, сделать API чуточку более «логичным».