Я активный сторонник Robot Framework. Уже писал на Хабре о том, что с его помощью можно решить практически любую задачу по автоматизации тестирования, особенно когда разработка ведется на Python. В той же статье я упоминал, что на смежных проектах в компании используется Pytest. Мне пришлось довольно близко познакомиться с этим инструментом, так что теперь я готов провести его полноценное сравнение с Robot Framework, конечно же, со своей персональной колокольни.

Robot vs. Snake by Beanhex (https://www.weasyl.com/~beanhex)
Robot vs. Snake by Beanhex (https://www.weasyl.com/~beanhex)

Кстати, на идею статьи меня натолкнул один из комментариев к предыдущей статье, в котором читатель сравнил jUnit и Robot Framework для проекта на Java. На мой взгляд, в этих условиях Robot Framework (основанный на Python) изначально был не очень применим. Но сама идея сравнения ближайшего аналога jUnit в мире Python (того самого Pytest) и Robot Framework мне понравилась.

Что это за инструменты?

Pytest

Фактически, Pytest - реализация xUnit фреймворка для Python. Если вы всю жизнь работали с jUnit или nUnit (двумя самыми распространенными представителями этого семейства для Java и .NET соответственно), то в Pytest найдете ровно те же стандарты - быстро поймете, что происходит, будете следовать привычным подходам написания юнит-тестов. xUnit-фреймворки - это надстройки над языком программирования или библиотеки в них, которые удобно использовать самим разработчикам. За счет этого они довольно распространены и популярны. 

Для понимания ситуации вместе с Pytest, как и вместе со многими xUnit-фреймворками, необходимо использовать инструмент генерации логов. Уже традиционно в этой роли применяется Allure. Наверное, сегодня это стандарт для логов автотестов, который хочет видеть бизнес. Но надо понимать, что Pytest разрабатывается своим сообществом, а Allure - своим, и векторы этих команд могут в какой-то момент развернуться в разные стороны.

Robot Framework

В отличие от Pytest, Robot Framework - это domain specific language (DSL) - язык, специфичный для своей предметной области. Это не Python, хотя он на нем построен. Зато на Python можно написать все, что угодно для Robot Framework. И все возможности, о которых я здесь говорю (а также интеграции, настройки), доступны из коробки.

Robot Framework не настолько удобен разработчикам, поскольку требует более глубокого погружения. По сути, это другой подход написания автотестов. Как Cucumber для Java. Зато Robot Framework самостоятельно генерирует развернутые отчеты (мы об этом еще поговорим), т.е. фактически он содержит в себе также слой генерации отчетов, и развивается продукт одним сообществом - неделимо и гармонично. 

Кстати, сообщество открытое и довольно отзывчивое. У него есть открытый канал в Slack, где каждый желающий может задать вопрос по Robot Framework и получить на него ответ. Я и сам иногда там отвечаю. Я даже иногда контрибьючу в Robot Framework.

Аргументы в пользу Robot Framework

Отсутствие лишних ограничений на наименования

В Pytest и ряде других xUnit фреймворков название тест-кейса всегда должно начинаться с test. Мне кажется логичным, когда используется метод test. Но в остальных случаях ограничения на наименования не помогают процессу. 

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

Но на мой взгляд, здесь проблема именно в выбранном подходе, который “тащит” за собой то, что в XXI веке нам приходится ставить какие-то ограничения на названия тест-кейсов.

Robot Framework никак не заточен на юнит-тесты. В нем предусмотрен раздел Keywords, а сами тесты могут называться как угодно. Мне кажется, так работать удобнее. Более того, в Robot Framework любые сущности, в том числе keywords, можно называть по-русски. Грамотно подобрав названия, текст теста можно превратить в рассказ (“Открой сайт, введи логин, убедись, что результат такой-то”). Это позволяет не писать длинные комментарии, рассказывающие о том, что там происходит. Любой участник разработки или руководитель, никогда не залезавший в недра тестирования, может открыть этот тест и понять его. И время экономится, и понимание ситуации даже через полгода сохраняется. 

Suite setup

Бывает, что для набора тест-кейсов (тест-сьюта) требуются однотипные настройки, например выполнение метода, который готовит некие данные, создает сущности и сохраняет их идентификаторы для использования в тестах. В Robot Framework предусмотрены отдельные настройки suite setup, которые применяются для всех тестов в наборе. Отдельно есть suite teardown, который выполняется после всех тестов в наборе, а также аналогичные настройки для каждого тест-кейса (test setup и test teardown). Это удобно, логично и не требует изобретения велосипедов.

В xUnit фреймворках есть аннотации, заменяющие настройки suite setup, а в Pytest есть фикстуры со scope=”class”

В Pytest тест-кейсы могут быть просто методами (и тогда у них нет никакого suite setup - т.е. нет единой подготовки). Если же нужна единая подготовка, мы можем обернуть эти методы в класс. Но если мы сделаем фикстуру со scope=”class” для этого класса (т.е. попытаемся реализовать suite setup), то получим отдельный инстанс класса для каждого теста, так что данные из suite setup никак не попадут в тест-кейсы. Отдельные инстансы, вероятно, создаются из предположения, что данные из разных тест-кейсов не должны влиять друг для друга. Но из-за этого настроить среду для выполнения тестов намного сложнее, чем в Robot Framework, где suite setup предусмотрен априори.

Обычно этот вопрос в Pytest приходится обходить через создание отдельной фикстуры, куда загружаются данные. Эта фикстура впоследствии подтягивается в тесты. Другой путь - воспользоваться средствами Python, создав статическое поле у класса, которое будет общим для всех его инстансов (например, self.__class__.test_id = 2). Но на мой взгляд, это тоже костыль - к полям класса через подчеркивание не стоит обращаться извне.

Логи

Как я отметил выше, в Pytest для генерации логов чаще всего используется Allure. Это красиво и модно. Но из-за описанной выше особенности с инстансами, Allure не понимает, что и как связано с тестом. В отчет не попадают действия из suite setup. Приходится это обходить через написание обработчиков. И с моей точки зрения это уже не просто костыль, а настоящий костылесипед. 

Кстати, в других xUnit фреймворках эта проблема тоже есть.

На фоне Pytest+Allure у Robot Framework логи максимально подробны и даже избыточны. Они включают даже то, о чем ты никогда не задумаешься - Robot пишет все, что ты делаешь. С помощью этого лога гораздо проще отлавливать плавающие ошибки, которые так просто не воспроизведешь. Ты точно знаешь, где и какое значение было у переменной, какой API вызвали. Зачастую и перезапускать тест не надо, чтобы понять, что происходит. Для Pytest в таких сложных случаях приходится придумывать инструменты, которые помогают генерировать лог, как у Robot Framework.

Синтаксический сахар

В Robot Framework не нужно придумывать усложняющих конструкций. Есть выражения, спасающие от лишнего кода. 

Я люблю писать keyword-обертки и в них оборачивать другие keyword-ы. Например, в keyword, который из ответа API берет ID, можно будет подсовывать keyword, который дергает разные API (если в компании есть стандарты написания кода, то ответы API будут сходными - поле ID скорее всего будет во всех).

Например, можно написать: “Идентификатор из создание сущности”. Здесь “Идентификатор из” - keyword-обертка, “создание сущности” - это keyword, который вызывает API и отдает весь ответ. В другом случае можно написать “Идентификатор из создание таблицы”, где “создание таблицы” - это другой keyword.

Для меня писать подобным образом удобнее и понятнее. А если изменить keyword с “создание сущности” на “создания сущности”, то конструкция будет читаться даже с литературной точки зрения (“Идентификатор из создания сущности”).

Теггирование

В заголовке лога Robot Framework показывает статистику по каждому тегу. Если изначально правильно расставить теги, достаточно будет взглянуть на эту статистику, чтобы увидеть, в чем проблема, не забивая себе голову тем, что происходит внутри. Я писал про это в предыдущей статье. Там же я упоминал, что теги можно привязывать к идентификаторам багов в Jira или в любом другом треккере, который у вас используется. Как раз недавно у меня был случай, когда на одном из поддерживаемых тестов, о которых я и думать забыл с полгода назад, “сработали” старые забытые баги, привязанные к тегам. И на их поиски ушло несколько секунд, вместо минут и часов.

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

Кстати, и Allure не дает возможности отобразить статистику по тегам. Вероятно, если бы в нем появилась такая возможность, она в моих глазах приблизила бы связку Pytest+Allure к Robot Framework по функциональности. Плюс был бы в том, что связка Pytest+Allure не требовала бы забивать головы консервативных разработчиков новым DSL. К сожалению, появление таких инструментов маловероятно из-за того, что Pytest и Allure развиваются разными группами.

Аргументы в пользу Pytest

В моем списке всего два аргумента в пользу Pytest. Зато от сторонника другого инструмента они должны прозвучать весомо.

Отладка

Об этом писал один из читателей в комментариях к предыдущей статье. К сожалению, ты не можешь приостановить выполнение в Robot Framework, чтобы посмотреть значения переменных. Это факт, который можно записать в плюс Pytest.

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

Параметрические тесты Pytest

Я нашел то, чего нет в Robot Framework - благодаря параметризации Pytest может создавать тест-кейсы на лету. В Robot Framework 10 тестов всегда будут 10-ю тестами, ни больше, ни меньше. В Pytest в некоторых случаях с помощью одного параметрического теста можно покрыть огромное количество кейсов.

На примере реального проекта. Предположим, у нас есть API с двумя параметрами, каждый из которых может принимать несколько вариантов значений (например, один принимает 7 значений, другой - 10). И эти значения друг друга не исключают. В соответствии с теорией тестирования в таком случае надо выбрать несколько кейсов, более-менее равномерно покрывающих сетку из 70 “пересечений” (метод  pair-wise). Но я с помощью метода product из модуля itertools (который перемножает списки) написал тест-сетап, который подготавливает 70 комбинаций данных, а потом ходит в API и обеспечивает то самое утопическое exhaustive testing. При появлении еще одного варианта в начальных данных мне достаточно просто добавить строчку в один из первоначальных списков.

В Robot Framework так сделать нельзя. Там можно написать тест-шаблон, который принимает два значения, и сделать 70 вызовов. При появлении новых значений количество вызовов придется увеличивать.

В целом плюсов за Robot Framework получилось больше - я остаюсь его ярым сторонником. Но, конечно, это лишь мое мнение.

Автор статьи: Владимир Васяев

P.S. Мы публикуем наши статьи на нескольких площадках Рунета. Подписывайтесь на наши страницы в VK, FB, Instagram или Telegram-канал, чтобы узнавать обо всех наших публикациях и других новостях компании Maxilect.