...
суббота, 25 ноября 2017 г.
пятница, 24 ноября 2017 г.
четверг, 23 ноября 2017 г.
среда, 22 ноября 2017 г.
вторник, 21 ноября 2017 г.
понедельник, 20 ноября 2017 г.
[Перевод] Jest и Puppeteer: автоматизация тестирования веб-интерфейсов
Здесь речь пойдёт об основах работы с Puppeteer и Jest на примере тестирования веб-формы. Также тут будут рассмотрены особенности использования Chromium с пользовательским интерфейсом и без него, и некоторые полезные мелочи, касающиеся различных аспектов тестирования веб-страниц и организации рабочей среды. Валентино полагает, что, хотя Puppeteer — инструмент сравнительно новый и его API вполне может подвергаться изменениям, у него есть шанс занять достойное место в арсенале веб-разработчиков.
О некоторых особенностях рассматриваемых тестов
Недавно я писал тесты интерфейсов и в это время наткнулся на пост Кента С. Доддса, посвящённый повышению стабильности тестов за счёт использования атрибута
data-*
. Это, если в двух словах, пользовательские атрибуты, которые можно задавать для практически любых HTML-элементов. Они особенно полезны при организации обмена данными с JavaScript-программами.
Материал Кента попался мне очень вовремя, так как я тогда пользовался примерно такими конструкциями:
await page.waitForSelector("#contact-form");
await page.click("#name");
await page.type("#name", user.name);
Тут надо отметить, что я, в основном, занимаюсь серверным программированием. И хотя я пока не агитирую за использование
data-*
в тестах, я должен признать, что это, всё-таки, отличный подход. Особенно полезно это в крупных приложениях, но пока, в нашем простом примере, я будут использовать классический способ обращения к элементам.
Тестирование формы обратной связи
Итак, наша цель заключается в тестировании формы обратной связи на данной странице, работой над которой я занимаюсь. Вот эта форма:
Она включает в себя следующие элементы:
- Поле для ввода имени.
- Поле для ввода адреса электронной почты.
- Поле для ввода телефона.
- Область для ввода произвольного текста.
- Флажок, устанавливая который, пользователь соглашается с правилами обработки данных.
- Кнопка отправки формы.
В ходе тестирования нужно проверить работоспособность формы, а именно, убедиться в том, что посетитель сайта сможет ей воспользоваться для отправки запроса в компанию.
Настройка проекта
Для начала поближе рассмотрим инструменты, которыми будем пользоваться для автоматизации тестирования.
Jest — фреймворк для тестирования, разработанный Facebook. Jest даёт платформу для автоматизированного тестирования, а также базовую библиотеку, позволяющую строить утверждения (Expect).
Puppeteer — библиотека для Node.js, которая позволяет управлять браузером Chromium без пользовательского интерфейса. Инструмент это довольно новый, поэтому самое время его опробовать и подумать над тем, нужен ли он в конкретном проекте, и если нужен — о том, как встроить его в существующую экосистему.
Faker — библиотека для Node.js, которая умеет генерировать случайные данные. Среди них — имена, телефоны, адреса. Это, кстати, нечто вроде Faker для PHP.
Если у вас уже есть проект, на котором вы хотите поэкспериментировать, установить необходимые библиотеки можно такой командой:
npm i jest puppeteer faker --save-dev
Установка Puppeteer займёт некоторое время, так как, кроме прочего, в ходе установки библиотеки устанавливается и браузер Chromium.
Chromium — это веб-браузер с открытым исходным кодом, который является основой Google Chrome. Chromium и Chrome имеют практически одинаковые возможности, основные отличия заключаются в особенностях лицензирования.
После того, как всё необходимое будет установлено, настроим Jest в package.json
. Команда test
должна указывать на исполняемый файл Jest:
"scripts": {
"test": "jest"
}
Кроме того, в Jest я предпочитаю пользоваться такой конструкцией:
import puppeteer from "puppeteer";
Поэтому тут нам понадобится Babel для Jest:
npm i babel-core babel-jest babel-preset-env --save-dev
После установки Babel создадим в папке проекта файл
.babelrc
со следующим содержимым:
{
"presets": ["env"]
}
На этом предварительная подготовка завершена и мы можем приступать к написанию тестов.
Пишем тесты
Создадим новую директорию в папке проекта. Назвать её можно
test
или spec
. Затем, в этой директории, надо создать файл form.spec.js
.
Теперь предлагаю рассмотреть код тестов по частям, начав с секции импорта. Ниже я приведу весь этот код целиком.
Сначала импортируем Faker и Puppeteer:
import faker from "faker";
import puppeteer from "puppeteer";
Теперь зададим URL формы. Возможно, вы решите протестировать версию формы, которая находится в разработке и доступна локально, вместо того, чтобы подключаться к рабочему сайту:
const APP = "http://ift.tt/2AWAIcU"
Теперь, с помощью Faker, создаём фиктивного пользователя:
const lead = {
name: faker.name.firstName(),
email: faker.internet.email(),
phone: faker.phone.phoneNumber(),
message: faker.random.words()
};
Дальше — определяем некоторые переменные, необходимые для работы с Puppeteer:
let page;
let browser;
const width = 1920;
const height = 1080;
Теперь настраиваем поведение Puppeteer:
beforeAll(async () => {
browser = await puppeteer.launch({
headless: false,
slowMo: 80,
args: [`--window-size=${width},${height}`]
});
page = await browser.newPage();
await page.setViewport({ width, height });
});
afterAll(() => {
browser.close();
});
Здесь мы пользуемся методами Jest
beforeAll
и afterAll
. Первый нам нужен из-за того, что перед выполнением тестов требуется запустить, с помощью Puppeteer, браузер. После запуска браузера мы можем открыть новую страницу. Когда тесты завершатся, браузер должен быть закрыт. Делается это в методе afterAll
с помощью команды browser.close()
.
Надо отметить, что мы не ограничены лишь методами beforeAll
и afterAll
. Для того, чтобы узнать о других возможностях Jest, взгляните на документацию к этой библиотеке. В любом случае, рекомендуется пользоваться одним экземпляром браузера для выполнения всего набора тестов, вместо того, чтобы открывать и закрывать браузер для каждого отдельного теста.
Тут мне хотелось бы сделать некоторые комментарии по поводу вышеприведённого фрагмента кода. А именно, обратите внимание на то, что я запускаю браузер в оконном режиме, используя параметр headless: false
. В данном случае так сделано для того, чтобы иметь возможность записать происходящее на экране на видео и показать процесс тестирования. Выполняя реальные тесты с помощью описываемых инструментов обычно незачем наблюдать за тем, что происходит. Для того, чтобы браузер запускался без интерфейса, можно просто убрать параметры, используемые при вызове метода launch()
.
То же самое касается и команды setViewPort()
, которую тоже можно убрать. Или, что даже лучше, можно настроить два разных окружения тестирования. Одно использовать для визуальной отладки (речь об этом пойдёт ниже), второе — для работы с браузером без пользовательского интерфейса.
Теперь пишем код тестов:
describe("Contact form", () => {
test("lead can submit a contact request", async () => {
await page.waitForSelector("[data-test=contact-form]");
await page.click("input[name=name]");
await page.type("input[name=name]", lead.name);
await page.click("input[name=email]");
await page.type("input[name=email]", lead.email);
await page.click("input[name=tel]");
await page.type("input[name=tel]", lead.phone);
await page.click("textarea[name=message]");
await page.type("textarea[name=message]", lead.message);
await page.click("input[type=checkbox]");
await page.click("button[type=submit]");
await page.waitForSelector(".modal");
}, 16000);
});
Обратите внимание на возможность использования конструкции async/await с Jest. Тут предполагается, что тестирование проводится с использованием одной из свежих версий Node.js.
Рассмотрим эти тесты. Вот какие действия выполняет браузер, управляемый программно:
- Переход по адресу, заданному в константе APP.
- Ожидание появления формы обратной связи.
- Щелчки по полям и заполнение их данными.
- Установка флажка.
- Отправка формы.
- Ожидание появления модального окна.
Обратите внимание на то, что функции Jasmine
test()
, в качестве второго параметра, передан тайм-аут (16000). Это позволяет наблюдать за тем, как именно браузер работает со страницей.
Если выполнять тестирование с использованием браузера, видимого на экране, и не задать при этом тайм-аут, возникнет следующая ошибка:
Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL
Если выполнять тестирование, запуская браузер без интерфейса, тайм-аут можно убрать.
Теперь всё готово и тестирование можно запустить следующей командой:
npm test
После этого остаётся лишь наблюдать за браузером, который сам работает со страницей.
Если кому интересно, это экранное видео было записано в Fedora с помощью
recordmydesktop
и такой команды:
recordmydesktop --width 1024 --height 768 -x 450 -y 130 --no-sound
Однако, это ещё не всё.
Тестирование других элементов интерфейса
Теперь, когда с формой мы разобрались, можно протестировать ещё какие-нибудь элементы страницы.
Выясним, как обстоят дела с тем, что находится в теге . Как известно, там должен быть осмысленный заголовок страницы:
describe("Testing the frontend", () => {
test("assert that