Когда проект использует Puppeteer для тестирования, важно заранее продумать организацию кода. Это гарантирует удобство работы с проектом, улучшает читаемость и поддержку кода, а также упрощает его масштабирование. В этой части рассматриваются лучшие практики по организации кода, которые помогут сделать ваш проект на Puppeteer более структурированным и устойчивым к изменениям.
Создание структуры каталогов
Правильная структура каталогов — основа любого успешного проекта. Для тестов с Puppeteer хорошая практика — разделять код на несколько каталогов, каждый из которых отвечает за свою часть приложения.
Рекомендуемая структура каталогов:
/project
/tests
/e2e
/unit
/pages
/utils
/data
/config
/node_modules
package.json
/tests — основной каталог для тестов. Его можно разделить на подкаталоги в зависимости от типа тестирования: e2e (end-to-end), unit (модульные тесты), интеграционные тесты.
/pages — хранит страницы и компоненты, с которыми взаимодействует Puppeteer. Это помогает реализовать концепцию «Page Object Model» (POM).
/utils — каталог для вспомогательных утилит и функций.
/data — каталог для хранения тестовых данных, например, JSON-файлов.
/config — конфигурационные файлы, например, для настройки среды выполнения Puppeteer.
Такой подход упрощает ориентирование в проекте, а также помогает избежать дублирования кода.
Для упрощения работы с тестами и обеспечения их повторяемости важно использовать паттерн Page Object Model. Этот паттерн предлагает создавать отдельные классы для каждой страницы, с которой взаимодействует тест, инкапсулируя логику взаимодействия с элементами страницы.
Пример реализации POM:
// /pages/LoginPage.js class LoginPage { constructor(page) {
this.page = page; this.usernameField = & this.passwordField =
'#password'; this.submitButton = '#submit'; }
async navigate() { await this.page.goto('https://example.com/login');
}
async login(username, password) { await
this.page.type(this.usernameField, username); await
this.page.type(this.passwordField, password); await
this.page.click(this.submitButton); } }
module.exports = LoginPage;
Каждая страница приложения представляется как объект, который инкапсулирует взаимодействие с ней. В тестах эти объекты используются для выполнения действий на страницах.
// /tests/e2e/login.test.js
const puppeteer = require('puppeteer');
const LoginPage = require('../. ./pages/LoginPage');
describe('Login functionality', () => {
let browser;
let page;
let loginPage;
beforeAll(async () => {
browser = await puppeteer.launch();
page = await browser.newPage();
loginPage = new LoginPage(page);
});
afterAll(async () => {
await browser.close();
});
test('should allow user to login with valid credentials', async () => {
await loginPage.navigate();
await loginPage.login('user', 'password');
// Далее, добавьте проверки, что страница перешла на ожидаемый URL или что элементы появились
});
});
С помощью Page Object Model можно не только повысить повторное использование кода, но и улучшить поддержку, так как изменения на странице (например, изменение селекторов) потребуется вносить только в одном месте.
Создание вспомогательных утилит поможет уменьшить количество повторяющегося кода в тестах. Это могут быть функции для ожидания элементов, выполнения действий или выполнения настроек.
Пример утилиты для ожидания элементов:
// /utils/waitForElement.js async function waitForElement(page,
selector, timeout = 30000) { try { await page.waitForSelector(selector,
{ timeout }); } catch (error) { throw new
Error(Element with selector "${selector}" was not found within ${timeout}ms);
} }
module.exports = waitForElement;
Использование утилиты в тестах:
const waitForElement = require('../. ./utils/waitForElement');
test('should display the login button', async () => {
await waitForElement(page, '#loginButton');
// После этого можно выполнить проверку, что кнопка действительно появилась
});
Такие утилиты значительно упрощают тесты и делают их более читаемыми.
Для улучшения гибкости и удобства тестирования полезно вынести конфигурацию Puppeteer в отдельные файлы, чтобы можно было легко изменять параметры (например, запускать браузер в режиме без головы, устанавливать конкретные тайм-ауты или настройку прокси).
Пример конфигурации:
// /config/puppeteerConfig.js
module.exports = {
headless: true,
slowMo: 50,
timeout: 30000,
};
При запуске тестов эти настройки можно будет применять для каждого нового браузера:
const puppeteer = require('puppeteer'); const puppeteerConfig =
require('../config/puppeteerConfig');
beforeAll(async () => { browser = await
puppeteer.launch(puppeteerConfig); });
Такой подход позволяет гибко настраивать тестирование и быстро адаптировать проект под новые требования.
Разделение тестов на разные категории помогает избежать перегрузки и сделать код тестов более понятным. Основные типы тестов:
Каждый тип тестов имеет свою цель и помогает сосредоточиться на разных аспектах приложения. Хорошая практика — использовать разные подходы и инструменты для разных типов тестов, например, для e2e тестов можно использовать Puppeteer, а для модульных — Jest или Mocha.
Параллельное выполнение тестов может значительно ускорить процесс тестирования, особенно при большом количестве тестов. Для этого можно использовать такие инструменты, как Jest с его возможностями параллельного выполнения тестов, или же интегрировать Puppeteer с TestCafe или Playwright для более сложных сценариев.
Пример параллельного запуска с Jest:
{
"jest": {
"maxWorkers": "50%" // Параллельное выполнение на 50% доступных ядер
}
}
Запуск тестов параллельно позволяет сократить время на выполнение тестов, но важно, чтобы тесты не зависели друг от друга и были независимыми.
Интеграция с системами CI/CD позволяет автоматизировать процесс тестирования и развертывания. Популярные системы, такие как GitHub Actions, GitLab CI, CircleCI, позволяют настроить автоматический запуск тестов Puppeteer при каждом коммите в репозиторий.
Пример конфигурации для GitHub Actions:
name: Run Puppeteer Tests
on: push: branches: - main pull_request: branches: - main
jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 -
name: Set up Node.js uses: actions/setup-node@v2 with: node-version:
'14' - run: npm install - run: npm test
Этот процесс позволяет гарантировать, что тесты будут выполняться автоматически после каждого изменения в коде, а также повышает надежность сборок.
Правильная обработка ошибок и логирование тестов — неотъемлемая часть структуры проекта. Понятные сообщения об ошибках, которые точно указывают на место сбоя, облегчают анализ и исправление проблем.
Использование логирования ошибок и создание отчетов о тестах с помощью таких библиотек, как Jest или Mocha, поможет улучшить процесс отладки и повышения качества кода.
const { expect } = require('chai');
describe('Login test', () => { it('should show error for invalid
credentials', async () => { try { await
loginPage.login('invalidUser', 'wrongPassword'); const error = await
page.$('.error-message'); expect(error).to.not.be.null; } catch (error)
{ console.error('Test failed', error); throw error; } }); });
Внедрение автоматических отчетов также помогает быстрее выявлять проблемные места.
Использование этих принципов и рекомендаций позволит организовать проект с Puppeteer так, чтобы тесты оставались понятными, масштабируемыми и удобными для поддержания в долг