DevGang
Авторизоваться

Как добавить в свой проект тестирование снимков экрана с помощью Cypress

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

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

Моя мотивация к созданию этой стратегии тестирования пришла из работы. В Thinkific у нас есть внутренняя система дизайна, и мы добавили Cypress, чтобы избежать сюрпризов при работе с файлами CSS / JS.

К концу поста у нас будут PR с тестами Cypress:

Прежде чем мы начнем

Я создал образец веб-сайта, имитирующий библиотеку компонентов. Это очень простой веб-сайт, созданный с помощью TailwindCSS и размещенный на Vercel. Он документирует 2 компонента: значок и кнопку.

Вы можете ознакомиться с исходным кодом на GitHub. Сайт статичен и находится внутри папки public. Вы можете увидеть сайт локально, запустив npm run serve и выполнив проверку в браузере http: // localhost: 8000.

Добавление Cypress Image Snapshot и Cypress

Начните с клонирования репозитория примеров. Затем создайте новую ветку и установите Cypress Image Snapshot, пакет, отвечающий за захват / сравнение снимков экрана.

git checkout -b add-cypress
npm install -D cypress cypress-image-snapshot

После добавления пакетов необходимо выполнить несколько дополнительных шагов, чтобы добавить Cypress Image Snapshot в Cypress.

Создайте файл cypress/plugins/index.js со следующим содержанием:

const { addMatchImageSnapshotPlugin } = require('cypress-image-snapshot/plugin');

module.exports = (on, config) => {
  addMatchImageSnapshotPlugin(on, config);
};

Затем создайте файл cypress/support/index.js, содержащий:  

import { addMatchImageSnapshotCommand } from 'cypress-image-snapshot/command';

addMatchImageSnapshotCommand();

Создание теста скриншота

Пришло время создать тестовый скриншот. Вот план:

  1. Cypress посетит каждую страницу (значок и кнопку) проекта.
  2. Cypress сделает снимок экрана каждого примера на странице. На странице значка есть 2 примера (по умолчанию и таблетка), а на странице кнопки есть 3 примера (по умолчанию, таблетка и схема). Все эти примеры находятся внутри элемента <div> с расширением cypress-wrapper. Этот класс был добавлен с единственной целью - определить, что нужно протестировать.

Первым шагом является создание файла конфигурации Cypress ( cypress.json):

{
  "baseUrl": "http://localhost:8000/",
  "video": false
}

Это веб-сайт baseUrl, работающий локально. Как я уже упоминал ранее, npm run serve будет обслуживать содержимое папки public. Второй вариант, video отключает запись видео Cypress, которую мы не будем использовать в этом проекте.

Пришло время создать тест. В cypress/integration/screenshot.spec.js, добавьте:

const routes = ['badge.html', 'button.html'];

describe('Component screenshot', () => {
  routes.forEach((route) => {
    const componentName = route.replace('.html', '');
    const testName = `${componentName} should match previous screenshot`;

    it(testName, () => {
      cy.visit(route);
  
      cy.get('.cypress-wrapper').each((element, index) => {
        const name = `${componentName}-${index}`;
  
        cy.wrap(element).matchImageSnapshot(name);
      });
    });
  });
});

В приведенном выше коде я динамически создаю тесты на основе массива routes. Тест создаст одно изображение для каждого элемента .cypress-wrapper страницы.

Наконец, внутри package.json давайте создадим команду для запуска тестов:

{
  "test": "cypress"
}

Отсюда есть 2 варианта: запустить Cypress в автономном режиме npm run cypress run или использовать Cypress Test Runner с npm run cypress open.

Headless вариант

При использовании npm run cypress run вывод должен быть похож на следующее изображение:

Тесты пройдут, и в папке /snapshots/screenshot.spec.js будут созданы 5 изображений.

Вариант Test Runner

Используя npm run cypress open, откроется Cypress Test Runner, и вы сможете шаг за шагом следить за тестами.

Наша первая веха пройдена, поэтому давайте объединим эту ветку в master. Если вы хотите увидеть проделанную работу, перейдите в мой Pull Request.

Использование Cypress внутри Docker

Если вы запустите вышеуказанный тест попеременно между Headless и Test Runner, вы можете заметить, что снимок экрана будет отличаться.

Используя Test Runner с компьютером с дисплеем Retina, вы можете получить изображения (2x), в то время как режим Headless не дает вам скриншотов высокого качества.

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

Например, в Linux и Windows есть приложения с видимыми полосами прокрутки, а в macOS полоса прокрутки скрыта.

Если содержимое, захваченное на снимке экрана, не подходит для компонента, у вас может быть полоса прокрутки, а может и нет. Если ваш проект использует шрифты ОС по умолчанию, снимки экрана также будут отличаться в зависимости от среды.

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

Начнем с создания новой ветки:

git checkout -b add-docker

Cypress предлагает различные образы Docker - вы можете проверить подробности в их документации и их блоге.

В этом примере я буду использовать изображение cypress/included, которое включает Electron и готово к использованию.

Нам нужно внести два изменения: изменить baseUrl в файле сypress.json:

{
  "baseUrl": "http://host.docker.internal:8000/",
}

  и команду test в файле package.json:  

{
  "test": "docker run -it -e CYPRESS_updateSnapshots=$CYPRESS_updateSnapshots --ipc=host -v $PWD:/e2e -w /e2e cypress/included:4.11.0"
}

Запуск npm run test принесет нам проблему:

Изображения немного отличаются, но почему? Посмотрим, что внутри папки __diff_output__:

Как я уже упоминал ранее, несоответствия в типографике! Компонент Button использует шрифт ОС по умолчанию. Поскольку Docker работает внутри Linux, визуализированный шрифт не будет таким, который я установил в macOS.

Поскольку сейчас мы перешли на Docker, эти скриншоты устарели. Время обновить снимки:

CYPRESS_updateSnapshots=true npm run test

Обратите внимание, что я добавляю к команде test префикс переменной среды CYPRESS_updateSnapshots.

Второй этап пройден. Если вам нужна помощь, ознакомьтесь с моим pull request.

Давайте объединим эту ветку и двинемся дальше.

Добавление CI

Наш следующий шаг - добавление тестов в CI. На рынке есть различные решения CI, но для этого урока я буду использовать Semaphore. Я не связан с ними и использую их продукт на работе, так что это был для меня естественный выбор.

Конфигурация проста и может быть адаптирована к другим решениям, таким как CircleCI или Github Actions.

Прежде чем мы создадим наш файл конфигурации Semaphore, давайте подготовим наш проект к запуску в CI.

Первый шаг - установка start-server-and-test. Как указано в названии пакета, он запустит сервер, дождется URL-адреса, а затем выполнит тестовую команду:

npm install -D start-server-and-test

Во-вторых, отредактируйте файл package.json:  

{
  "test": "docker run -it -e CYPRESS_baseUrl=$CYPRESS_baseUrl -e CYPRESS_updateSnapshots=$CYPRESS_updateSnapshots --ipc=host -v $PWD:/e2e -w /e2e cypress/included:4.11.0",
  "test:ci": "start-server-and-test serve http://localhost:8000 test"
}

В скрипт test мы добавляем переменную окружения CYPRESS_baseUrl. Это позволит нам динамически изменять базовый URL, используемый Cypress. Также мы добавляем скрипт test:ci, который будет запускать только что установленный пакет.

Мы готовы к Semaphore. Создайте файл .semaphore/semaphore.yml со следующим содержимым:

 1 version: v1.0
 2 name: Cypress example
 3 agent:
 4   machine:
 5     type: e1-standard-2
 6     os_image: ubuntu1804
 7 blocks:
 8   - name: Build Dependencies
 9     dependencies: []
10     task:
11       jobs:
12         - name: NPM
13           commands:
14             - sem-version node 12
15             - checkout
16             - npm install
17   - name: Tests
18     dependencies: ['Build Dependencies']
19     task:
20       prologue:
21         commands:
22           - sem-version node 12
23           - checkout
24       jobs:
25         - name: Cypress
26           commands:
27             - export CYPRESS_baseUrl="http://$(ip route | grep -E '(default|docker0)' | grep -Eo '([0-9]+\.){3}[0-9]+' | tail -1):8000"
28             - npm run test:ci

Подробная разбивка конфигурации:

  1. Строки 1-6 определяют, какой тип экземпляра мы будем использовать в их среде.
  2. Строки 8 и 16 создают 2 блока: первый блок, «Build Dependencies», будет запущен npm install, загружая нужные нам зависимости. Второй блок, «Tests», будет запускать Cypress с некоторыми отличиями.
  3. В строке 27 мы динамически устанавливаем переменную окружения CYPRESS_baseUrl в зависимости от того, какой IP Docker использует в данный момент. Это заменит http://host.docker.internal:8000/, что может не работать во всех средах.
  4. В строке 28 мы, наконец, запускаем тест, используя start-server-and-test: как только сервер будет готов к подключению, Cypress запустит набор тестов.

Еще одна веха пройдена, пора объединить нашу ветку! Вы можете проверить Pull request, который содержит все файлы из этого раздела, и проверить сборку внутри Semaphore.

Запись тестов в cypress.io

Чтение вывода тестов в CI не очень дружелюбно. На этом этапе мы интегрируем наш проект с cypress.io.

Следующие шаги основаны на документации Cypress.

Начнем с получения идентификатора проекта и ключа записи. В терминале создайте новую ветку и запустите:

git checkout -b add-cypress-recording
CYPRESS_baseUrl=http://localhost:8000 ./node_modules/.bin/cypress open

Ранее я упоминал, что мы будем использовать Cypress внутри Docker. Но здесь мы открываем Cypress локально, поскольку это единственный способ интеграции с панелью управления веб-сайта.

Внутри Cypress перейдите на вкладку «Runs», нажмите «Set up project to record» и выберите имя и доступность. Мы получим projectId автоматически добавляемый в файл cypress.json и закрытый ключ записи. Вот видео с шагами:

В Semaphore я добавил ключ записи как переменную среды с именем CYPRESS_recordKey. Теперь давайте обновим наш тестовый сценарий, чтобы использовать переменную:  

{
  "test:ci": "start-server-and-test 'serve' 8000 'npm run test -- run --record --key $CYPRESS_recordKey'"
}

Это почти все, что нужно сделать. В Pull request мы видим интеграцию cypress.io в комментариях. Есть даже глубокая ссылка, которая ведет нас на их панель управления и показывает все скриншоты. Посмотрите видео ниже:  

Пора объединить нашу работу, и это конец нашей интеграции.

Тестирование в реальной жизни

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

В примере веб-сайта давайте удвоим горизонтальный отступ с 16 до 32 пикселей. Это изменение довольно просто, поскольку мы используем Tailwind CSS: px-4 заменяется на px-8. Вот этот Pull request.

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

Файл сравнения показывает исходный снимок экрана слева, текущий результат справа, и они объединены в середине. У нас также есть возможность загрузить изображение, чтобы лучше понять проблему:

Если это не проблема, обновите скриншоты:

CYPRESS_updateSnapshots=true npm run test

Конец

На сегодня все. Я надеюсь, что вы узнали, как Cypress может быть полезен, чтобы гарантировать, что никто не добавит неожиданные изменения в проект.

Источник:

#Testing
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

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

Попробовать

Оплатив хостинг 25$ в подарок вы получите 100$ на счет

Получить