Статьи

Визуальное регрессионное тестирование с PhantomCSS

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

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

<div>Hello, %username%!</div>

Чтобы убедиться, что это работает, вы можете (и должны) протестировать фрагмент кода, который генерирует сообщение, проверяя, что оно вставляет правильное имя. Вы также можете написать функциональный тест, используя Selenium или Protractor, чтобы проверить, присутствует ли элемент на странице с правильным текстом. Но этого недостаточно. Мы хотим проверить не только то, что текст генерируется правильно или отображается в DOM, но и убедиться, что весь элемент выглядит правильно , т.е. убедиться, что элемент не скрыт display: none Для этого есть ряд инструментов, но сегодня мы рассмотрим один из вариантов — PhantomCSS .

Что такое PhantomCSS?

PhantomCSS — это инструмент Node.js для проведения визуального регрессионного тестирования. Это открытый исходный код, разработанный парнями из Huddle . PhantomCSS позволяет вам запустить браузер без головы, открыть страницу и сделать снимок экрана всей страницы или определенного элемента на странице. Этот снимок экрана будет сохранен как базовое изображение для дальнейшего использования. Всякий раз, когда вы что-то меняете на сайте, вы можете снова запускать PhantomCSS. Он сделает еще один скриншот и сравнит его с исходным изображением. Если различий не обнаружено, тест будет пройден. Однако, если скриншоты не совпадают, тест не пройден, и для просмотра будет создано новое изображение, показывающее разницу. Такой подход делает этот инструмент идеальным для тестирования изменений в CSS.

PhantomCSS построен на основе нескольких ключевых компонентов:

  • CasperJS — инструмент для взаимодействия с браузером PhantomCSS или SlimerJS. Это позволяет вам открывать страницу и выполнять взаимодействия с пользователем, такие как нажатие на кнопки или ввод значений. Кроме того, CasperJS предоставляет собственную платформу для тестирования и возможность снимать скриншоты страницы.
  • PhantomJS 2 или SlimerJS — два разных безголовых браузера, каждый из которых можно использовать с PhantomCSS. Безголовый браузер похож на обычный браузер без пользовательского интерфейса.
  • Resemble.js — библиотека для сравнения изображений.

PhantomCSS можно использовать вместе с PhantomJS и SlimerJS, но в этой статье мы будем использовать PhantomJS.

Давайте возьмем это за спин

Давайте настроим небольшой тестовый проект, чтобы увидеть, как мы можем использовать этот инструмент на практике. Для этого нам понадобится веб-страница для тестирования и простой веб-сервер Node.js, чтобы CasperJS мог открыть страницу.

Настройка тестового проекта

Создайте файл index.html

 <!doctype html>
<html>
  <head>
    <style>
      .tag {
        color: #fff;
        font-size: 30px;
        border-radius: 10px;
        padding: 10px;
        margin: 10px;
        width: 500px;
      }

      .tag-first {
        background: lightcoral;
      }

      .tag-second {
        background: lightskyblue;
      }
    </style>
  </head>

  <body>
    <div class="tag tag-first">The moving finger writes, and having written moves on.</div>
    <div class="tag tag-second">Nor all thy piety nor all thy wit, can cancel half a line of it.</div>
  </body>
</html>

Чтобы установить веб-сервер, инициализируйте проект npm и установите пакет http-server

 npm init
npm install http-server --save-dev

Чтобы запустить сервер, давайте определим простой скрипт npm. Просто добавьте следующий раздел scriptspackage.json

 "scripts": {
  "start": "http-server"
},

Теперь вы можете запустить npm starthttp://127.0.0.1:8080 Запустите сервер и оставьте его запущенным на данный момент. Нам это понадобится через некоторое время.

Установка PhantomCSS

Установить PhantomCSS легко, все, что вам нужно сделать, это добавить несколько зависимостей в ваш проект:

 npm install phantomcss casperjs phantomjs-prebuilt --save-dev

Создание набора тестов

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

 var phantomcss = require('phantomcss');

// start a casper test
casper.test.begin('Tags', function(test) {

  phantomcss.init({
    rebase: casper.cli.get('rebase')
  });

  // open page
  casper.start('http://127.0.0.1:8080/');

  // set your preferred view port size
  casper.viewport(1024, 768);

  casper.then(function() {
      // take the screenshot of the whole body element and save it under "body.png". The first parameter is actually a CSS selector
      phantomcss.screenshot('body', 'body');
  });

  casper.then(function now_check_the_screenshots() {
    // compare screenshots
    phantomcss.compareAll();
  });

  // run tests
  casper.run(function() {
    console.log('\nTHE END.');
    casper.test.done();
  });
});

Тест откроет http://127.0.0.1:8080/bodyscreenshots/body.png

После того, как у нас есть сам тест, остается только определить скрипт для его запуска. Давайте добавим следующий скрипт в package.jsonstart

 "test": "casperjs test test.js"

Теперь вы можете запустить его, выполнив следующую команду:

 npm test

Вывод, который вы увидите, должен выглядеть примерно так:

 Test file: test.js                                                              
# Tags
PASS Tags (NaN test)

New screenshot at ./screenshots/body_0.png

Must be your first time?
Some screenshots have been generated in the directory ./screenshots
This is your 'baseline', check the images manually. If they're wrong, delete the images.
The next time you run these tests, new screenshots will be taken.  These screenshots will be compared to the original.
If they are different, PhantomCSS will report a failure.

THE END.
WARN Looks like you didn't run any tests.                                       
npm ERR! Test failed.  See above for more details.

Поскольку вы запустили тест в первый раз, он просто создаст новый базовый скриншот и не будет выполнять никакого сравнения. Идите и загляните в папку со screenshots Вы должны увидеть изображение, подобное этому:

Скриншот всего тела

Это золотой стандарт того, как должен выглядеть ваш сайт, и будущие тесты будут сравнивать его результаты с этим изображением.

Представляем регрессию

Если вы снова запустите ту же тестовую команду, она сообщит, что все тесты прошли успешно:

 Test file: test.js                                                              
# Tags
PASS Tags (NaN test)


PASS No changes found for screenshot ./screenshots/body_0.png

PhantomCSS found 1 tests, None of them failed. Which is good right?

If you want to make them fail, change some CSS.

THE END.
PASS 1 test executed in 0.827s, 1 passed, 0 failed, 0 dubious, 0 skipped.

Этого и следовало ожидать, так как мы ничего не изменили на сайте. Давайте что-нибудь сломаем и снова запустим тесты. Попробуйте изменить некоторые стили в index.html Теперь давайте снова запустим тест и посмотрим, что произойдет:

 Test file: test.js                                                              
# Tags
PASS Tags (NaN test)
Failure! Saved to ./failures/body_0.fail.png


FAIL Visual change found for screenshot ./screenshots/body_0.png (11.41% mismatch)
#    type: fail
#    file: test.js
#    subject: false

PhantomCSS found 1 tests, 1 of them failed.

PhantomCSS has created some images that try to show the difference (in the directory ./failures). Fuchsia colored pixels indicate a difference between the new and old screenshots.

THE END.
FAIL 1 test executed in 1.082s, 0 passed, 1 failed, 0 dubious, 0 skipped.       

Details for the 1 failed test:

In test.js
  Tags
    fail: Visual change found for screenshot ./screenshots/body_0.png (11.41% mismatch)
npm ERR! Test failed.  See above for more details.

Здесь произошло несколько важных вещей. Во-первых, PhantomCSS сообщил, что тесты не пройдены из-за несоответствия скриншоту body_0.png Несоответствие составляет 11,41%. Во-вторых, разница между текущей и предыдущей версией была сохранена в папке с failures Если вы откроете его, вы увидите такой снимок экрана:

Снимок экрана с ошибкой

На скриншоте удобно выделены области, которые были изменены, поэтому легко заметить разницу.

Принятие изменений

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

 npm test -- --rebase

Обратите внимание на две двойные черты. Это способ передачи параметров в команду npm. Поэтому следующая команда приведет casperjs test test.js --rebase Теперь, когда мы приняли изменение, предыдущее базовое изображение будет заменено новым.

Продолжая

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

  • Собираетесь ли вы запускать тесты на реальном веб-сайте или на каком-нибудь руководстве по стилю, где присутствуют только отдельные элементы пользовательского интерфейса?
  • Есть ли у вашего сайта динамический контент? Если да, то изменения содержимого приведут к срыву тестов. Чтобы избежать этого, вам нужно настроить отдельную версию сайта со статическим контекстом для запуска тестов против него.
  • Собираетесь ли вы добавить скриншоты в свой контроль версий? Да, ты должен.
  • Собираетесь ли вы делать скриншоты целых страниц или отдельных элементов?

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