Cypress.io - объединение функций конвейера

В прошлом году я писал об альтернативном и безопасном для типов способе выполнения пользовательских команд с помощью cy.then. В LogRhythm мы усовершенствовали эту идею - разбиваем тесты сложного приложения на небольшие, составляемые части.

Он работает у нас больше года, но при использовании cy.then возникли некоторые проблемы:

  • Нет имен функций журналирования
  • Нет снимков DOM
  • Нет повторов
  • Никакого автоматического ожидания появления элементов

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

В спецификации обещаний указано, что функция, предоставленная then, запускается только один раз (Раздел 2.2.2.3 спецификации Promises / A +). Cypress предлагает такую ​​же гарантию для функций, предоставляемых методу cy.then. Есть 3 причины, по которым Cypress повторяет большинство пользовательских команд:

  • Функция выдает ошибку - Cypress будет пытаться выполнить функцию до тех пор, пока ошибка не исчезнет.
  • Список узлов пуст - все команды Cypress, которые работают с элементами DOM, неявно проверяют наличие хотя бы одного элемента в списке.
  • should вызывает повторную попытку команды до тех пор, пока не будет выполнено условие - большинство команд будут повторять попытку, если за ней следует .should, пока не пройдут условия утверждения (например: cy.get('#foo').should('be.visible'))

Вспомогательная функция могла бы предоставить собственное .should утверждение, чтобы заставить упаковку .then повторить попытку или подождать, но это только увеличило шум в журнале команд.

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

Уверенность

Нам нужно быть уверенным в наших автоматических тестах. Не только от разработчиков, но и от QA-инженеров и владельцев продуктов. Разделение теста на части позволяет создать DSL, более понятный для человека, который больше напоминает то, как мы сообщаем о действиях в тесте.

Вот пример теста с использованием только селекторов CSS:

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

Идентификаторы тестов семантических элементов и имена классов немного помогают понять, что делает тест, но для понимания цели разделов теста требуется интенсивное использование cy.log.

Далее используются вспомогательные функции pipe'd вместе с именами функций, которые более точно отражают то, как мы сообщаем о действиях, выполняемых в приложениях:

Вспомогательные функции размещаются в пространстве имен на объекте, чтобы помочь с автозаполнением VS Code. Как не разработчик, я могу начать читать, что делает тест, не разбираясь в этом с помощью селекторов DOM. Также в этом тесте есть два виртуализированных списка - список тревожных карт и раскрывающийся список статуса тревог. Виртуализированные списки можно безопасно протестировать, только зная детали внутренней реализации того, как работают виртуализированные списки. К счастью, эти виртуализированные компоненты экспортируют собственные вспомогательные функции, которые мы используем во вспомогательных функциях приложения, чтобы абстрагировать эти сложные взаимодействия до более простых помощников, таких как h.alarm.cards.getByName и h.selects.select.

Сочинение

Заманчиво создать большие вспомогательные функции, которые выполняют множество функций. То же правило для функций применяется к помощнику тестовой функции Cypress. Если в названии есть And, вероятно, это слишком много. Сначала у нас были помощники вроде getAlarmAndChangeStatus, которые получали первый сигнал тревоги на странице и меняли статус. Другой тест хотел получить первую тревогу и выполнить анализ тревог (посмотреть, какие события или журналы привели к срабатыванию тревоги). Другой тест хотел получить конкретный сигнал тревоги и изменить статус. Нам пришлось разбить первую функцию на более мелкие части, чтобы увеличить возможность повторного использования.

техническое обслуживание

Иногда мы меняем имена классов, идентификаторы тестов или структуру DOM. Использование необработанных селекторов CSS оказалось непростым делом. Наши тесты часто терпели неудачу из-за изменения структуры DOM, а не из-за наличия ошибки. Теперь вспомогательные функции можно тестировать независимо от работы всего приложения. Если реализация компонента изменилась, вспомогательную функцию можно изменить вместе с ней, что означает, что тесты все равно должны проходить.

Более простые команды

Интересным наблюдением является то, что многие команды Cypress на самом деле просто обертывают jQuery API. Большинство команд Cypress просто возвращают или просматривают дерево DOM, беря и возвращая обернутые jQuery списки узлов. Это означало, что мы могли использовать jQuery API непосредственно в тестовых вспомогательных функциях без повторной упаковки объекта. Пример:

На первый взгляд это кажется сложным. Это функция, которая принимает строку (имя) и возвращает функцию с именем «getAlarmByName», которая принимает $container в качестве входных данных. Имя функции важно для команды pipe, чтобы она знала, что помещать в журнал команд Cypress. Это функция, которую можно повторить с помощью команды pipe, пока она не вернет непустую коллекцию jQuery, и будет повторяться в дальнейшем, если за ней последуют какие-либо утверждения. Этот шаблон допускает очень маленькие и простые функции, которые просто используют jQuery API, но pipe дает функции обычные функции, которые поставляются со встроенными командами Cypress, такими как .find, включая записи в журнале команд и повторные попытки.

Этот конвейер функций также более точен по сравнению с цепочками команд Cypress - конвейерами. Интерфейс конвейера похож на оператор конвейера RxJS. Ваши пользовательские функции - это форма трубы, а «предмет» - это то, что проходит через каждую секцию трубы, изменяясь при движении.

Более подробную информацию можно найти на https://github.com/NicholasBoll/cypress-pipe.

Cypress-pipe устранил все недостатки, с которыми мы столкнулись при использовании .then:

  • Регистрирует имена функций и даже параметры с помощью декоратора loggable.
  • Снимки DOM (с выделением элементов, снимки до / после для асинхронных функций, использующих команды Cypress).
  • Повторяет попытку с .pipe: повторять до тех пор, пока не исчезнет ошибка, или повторять попытку, если за ней следует утверждение (неудачные попытки утверждения - это выданные ошибки).
  • Повторите попытку, если возвращается пустая коллекция jQuery (для продолжения должен существовать элемент).

Лучшая отладка

Cypress-pipe добавляет следующую отладочную информацию:

  • Имя функции (если у функции есть имя)
  • Последний использованный селектор jQuery, если элемент не найден (например, .foobar)
  • Время, затраченное Cypress на функцию (например, ожидание появления элемента в DOM)
  • Снимок DOM, когда конвейер определил значение - 1 снимок, если не использовались cy команды, до и после снимков, если использовались cy команды (конвейер предполагает, что это действие)
  • Снимки DOM включают возвращаемый элемент (если функция возвращает элемент). В случае действия переданный элемент DOM и возвращенный элемент DOM

Loggable добавляет следующее:

  • Аргументы, переданные в первую функцию карри (в противном случае теряются, потому что cy.pipe получает результат только этой первой функции)

Вот скриншот сбоя при использовании cy.pipe:

Вывод

Плагин Cypress pipe теперь находится в официальном списке плагинов Cypress: https://docs.cypress.io/plugins/. Некоторое время я пользовался им с большим успехом. Сдвиг парадигмы отличается от того, что я видел в Интернете, но легче сделать более стабильные тесты с лучшим обслуживанием из-за дополнительной информации в журнале команд. Все другие решения теряют возможность повторения и четность журнала команд.

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