Web Drivers
Web Drivers - это инструменты для взаимодействия с браузером.
- Интерфейс удаленного управления, который позволяет анализировать и управлять браузером
- Платформонезависимый и не зависит от языка
- Предоставляет набор интерфейсов для нахождения и управления элементами DOM
- Не имеет прямого отношения к тестированию
Selenium
Selenium — один из популярных фреймворков для тестирования. Поддерживается всеми основными платформами и на всех браузерах. Он позволяет автоматизировать тестирование, имитировать действия пользователей.
Использование:
import { Builder, By, Key, until } from 'selenium-webdriver';
describe('web driver', () => {
let driver;
test('google first result', async () => {
// Создаём инстанс вебдрайвера
driver = new Builder().forBrowser('chrome').build();
// Выполняем переход на страницу
driver.get('https://www.google.com');
// Получаем элемент ввода
const input = driver.findElement(By.name('q'));
// Вызываем на элементе события нажатия клавиш (ввод в поиск и Enter)
await input.sendKeys('hello', Key.ENTER);
// Дожидаемся пока не появится элемент и получаем его
const firstResult = await driver.wait(until.elementLocated(By.css('h3')), 10000);
// Выводим содержимое элемента
console.log(await firstResult.getAttribute('textContent'));
}, 10000);
afterEach(() => {
driver.quit();
});
});
В асинхронных запросах промисы должны возвращаться из тестов, иначе тесты не дожидаются выполнение асинхронных операций. Либо нужно использовать async await
import { Builder, By, Key } from 'selenium-webdriver';
const URL = 'http://svelte3-todo.surge.sh/';
describe('web driver', () => {
let driver;
// Создаём драйвер перед выполнением тестов
beforeAll(() => {
driver = new Builder()
.forBrowser('chrome')
.build();
});
// Тест добавления таска
test('add a task', async () => {
// Выполняем переход на страницу
driver.get(URL);
// Возвращаем промис из теста
return driver.findElement(By.className('js-todo-input')).sendKeys('Build App', Key.ENTER)
// Асинхнронные запросы оборачиваем в цепочку промиса
.then(() => driver.getPageSource())
.then((source) => {
expect(source.includes('Build App')).toBe(true);
});
}, 1000);
// Тест отметки таска как пройденного
test('mark a task complete', () => {
driver.get(URL);
return driver.findElement(By.className('js-todo-input')).sendKeys('Build App', Key.ENTER) // Создаём таск
// Перед изменением таска, проверяем что он не завершен, для этого проверяем класс
.then(() => driver.findElement(By.className('todo-item')).getAttribute('class')) // получаем класс, асинхронный запрос
.then((className) => {
// проверяем что класс не содержит 'done'
expect(className.includes('done')).toBe(false);
// Вызываем клик по задаче (отмечаем что она завершена)
// это асинхронная операция, поэтому возвращаем промис
return driver.findElement(By.css('label')).click();
})
// Снова получаем класс, асинхронный запрос, поэтому оборачиваем в цепочку then
.then(() => driver.findElement(By.className('todo-item')).getAttribute('class'))
.then((className) => {
// Проверяем имя класса
expect(className.includes('done')).toBe(true);
});
}, 1000);
test('delete a task', () => {
driver.get(URL);
// Перед удалением также создаем таск
return driver.findElement(By.className('js-todo-input')).sendKeys('Build App', Key.ENTER)
// Кликаем по кнопке удаления
.then(() => driver.findElement(By.className('delete-todo')).click())
// Получаем содержимое страницы
.then(() => driver.getPageSource())
.then((source) => {
// Проверяем что таск удалён
expect(source.includes('Build App')).toBe(false);
});
}, 1000);
afterAll(() => {
driver.quit();
});
});
Тоже самое с async await
:
import { Builder, By, Key } from 'selenium-webdriver';
const URL = 'http://svelte3-todo.surge.sh/';
describe('web driver', () => {
let driver;
// Создаём драйвер перед выполнением тестов
beforeAll(() => {
driver = new Builder()
.forBrowser('chrome')
.build();
});
// Тест добавления таска
test('add a task', async () => {
// Выполняем переход на страницу
driver.get(URL);
// Создаём таск
await driver.findElement(By.className('js-todo-input')).sendKeys('Build App', Key.ENTER);
const source = await driver.getPageSource();
expect(source.includes('Build App')).toBe(true);
}, 1000);
// Тест отметки таска как пройденного
test('mark a task complete', async () => {
driver.get(URL);
// Создаём таск
await driver.findElement(By.className('js-todo-input')).sendKeys('Build App', Key.ENTER);
// Перед изменением таска, проверяем что он не завершен, для этого проверяем класс
const classNameBefore = await driver.findElement(By.className('todo-item')).getAttribute('class'); // получаем класс
// проверяем что класс не содержит 'done'
expect(classNameBefore.includes('done')).toBe(false);
// Вызываем клик по задаче (отмечаем что она завершена)
await driver.findElement(By.css('label')).click();
// Снова получаем класс
const classNameAfter = await driver.findElement(By.className('todo-item')).getAttribute('class');
// Проверяем имя класса
expect(classNameAfter.includes('done')).toBe(true);
}, 1000);
test('delete a task', async () => {
driver.get(URL);
// Перед удалением также создаем таск
await driver.findElement(By.className('js-todo-input')).sendKeys('Build App', Key.ENTER);
// Кликаем по кнопке удаления
await driver.findElement(By.className('delete-todo')).click();
// Получаем содержимое страницы
const source = await driver.getPageSource();
// Проверяем что таск удалён
expect(source.includes('Build App')).toBe(false);
}, 1000);
afterAll(() => {
driver.quit();
});
});
Cypress
Cypress — это e2e фреймворк для тестирования на JS, имеет свой тест-раннер, поддерживает множество языков.
Компонентное тестирование:
import * as React from 'react';
import { mount } from '@cypress/react';
import Button from './Button.jsx';
it('Button', () => {
// Рендерим кнопку
mount(<Button>Text button</Button>);
// Кликаем по кнопке
cy.get('button').contains('Test button').click();
});
// Тестируем завершения таска
it('complete todo', () => {
// Отрываем страницу
cy.visit('/');
// Находим элемент ввода, имитируем ввод имени таска и нажатие Enter
cy.get('.new-todo').type('write tests{enter}');
// Отмечаем выполнение таска
cy.contains('.todo-list li', 'write tests').find('.toggle').check();
// Проверяем наличие класса
cy.contains('.todo-list li', 'write tests').should('have.class', 'completed');
// При установленном плагине cypress-plugin-snapshots можно создавать скриншоты
cy.get('.todoapp').toMatchImageSnapshot({
imageConfig: {
threshold: 0.001,
},
});
});
// Тест добавления таска
it('adds todos', () => {
// Открываем страницу
cy.visit('/');
// Создаём два таска
cy.get('.new-todo')
.type('write E2E tests{enter}')
.type('add API tests as needed{enter}');
// Проверяем что запросы были отправлены
cy.request('/todos')
.its('body')
.should('have.length', 2)
.and((items) => {
// ...
});
});
Playwright
Playwright — библиотека от Microsoft, так же поддерживает множество языков. Не имеет своего тестраннера.
Пример использования:
// Подключаем библиотеку
import playwright from 'playwright';
(async () => {
// Тестируем на разных браузеров в цикле
for (const browserType of ['chromium', 'firefox', 'webkit']) {
// Запускаем браузер, получаем инстанс браузера
const browser = await playwright[browserType].launch();
// Получаем контекст браузера
const context = await browser.newContext();
// Получаем инстанс страницы
const page = await context.newPage();
// Открываем страницу
await page.goto('https://mail.ru');
// Вызываем событие
await page.click('[data-testid="enter-password"]');
// Создаем скриншот
await page.screenshot({ path: `mail-${browserType}.png` });
// Закрываем браузер
await browser.close();
}
})();
Имитация другого устройства:
import { webkit, devices } from 'playwright';
const iPhone11 = devices['iPhone 11 Pro'];
describe(() => {
test('Main test', async () => {
// Запускаем браузер, получаем инстанс браузера
const browser = await webkit.launch();
// Получаем контекст устройства, задаём свои настройки
const context = await browser.newContext({
...iPhone11,
locale: 'en-US',
geolocation: { longitude: 12.492507, latitude: 41.889938 },
permissions: ['geolocation'],
});
// Получаем инстанс страницы
const page = await context.newPage();
// Открываем страницу
await page.goto('https://maps.google.com');
// Вызываем событие
await page.click('text="Your location"');
// Ждём выполнение запроса
await page.waitForRequest(/.*preview\/pwa/);
// Создаём скриншот
await page.screeshot({ path: 'iphone-11.png' });
// Закрываем браузер
await browser.close();
});
});
Puppeteer
Puppeteer — библиотека с упором на chrome. Синтаксис очень похож на playwright:
// Подключаем библиотеку
import puppeteer from 'puppeteer';
describe(() => {
test('Main test', async () => {
// Запускаем браузер и получаем инстанс браузера
const browser = await puppeteer.launch();
// Получаем инстанс страницы
const page = await browser.newPage();
// Открываем страницу
await page.goto('https://mail.ru');
// Вызываем событие
await page.click('[data-testid="enter-password"]');
// Создаем скриншот
await page.screenshot({ path: 'example.png' });
// Закрываем браузер
await browser.close();
});
});
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.