diff --git a/docs/basic-guides/parallelism.mdx b/docs/basic-guides/parallelism.mdx new file mode 100644 index 0000000..5c3fb84 --- /dev/null +++ b/docs/basic-guides/parallelism.mdx @@ -0,0 +1 @@ +# Parallelism diff --git a/i18n/ru/docusaurus-plugin-content-docs/current/basic-guides/parallelism.mdx b/i18n/ru/docusaurus-plugin-content-docs/current/basic-guides/parallelism.mdx new file mode 100644 index 0000000..e75a078 --- /dev/null +++ b/i18n/ru/docusaurus-plugin-content-docs/current/basic-guides/parallelism.mdx @@ -0,0 +1,352 @@ +# Параллельный запуск тестов + +## Параллельность в Testplane + +Параллельность в Testplane — это возможность одновременно выполнять несколько тестовых сценариев в разных браузерных сессиях, чтобы сократить общее время прогона тестов. + +### sessionsPerBrowser + +В конфигурационном файле объявляются типы браузеров в поле `browsers`. Для каждого типа браузера независимо настраивается, сколько экземпляров может работать одновременно: + +```typescript +// .testplane.config.ts +export default { + browsers: { + chrome: { + sessionsPerBrowser: 5, // до 5 параллельных сессий Chrome + }, + firefox: { + sessionsPerBrowser: 2, // до 2 параллельных сессий Firefox + }, + }, +}; +``` + +Параметр `sessionsPerBrowser` является ключевым в рамках управления параллельностью. Он определяет, сколько браузеров одного типа Testplane запустит одновременно. По умолчанию значение: `1`. + +### Воркеры + +Параметр `workers` определяет, сколько дочерних процессов-воркеров будет запущено. Архитектурно это выглядит так: + +- `Master`-процесс — координирует запуск, формирует очереди тестов и раздает задания воркерам +- `Worker`-процессы — непосредственно исполняют тесты + +Каждый воркер — это отдельный поток, но внутри одного воркера тесты выполняются конкурентно: когда тест ждет ответа от браузера (`await`), воркер не простаивает, а переключается на следующий тест. Поэтому даже один воркер способен обслуживать множество параллельных браузерных сессий. + +:::warning Важно +`workers` и `sessionsPerBrowser` — независимые ограничения. Увеличение числа воркеров не увеличивает реальную параллельность, если `sessionsPerBrowser` остается прежним. +::: + +### testsPerSession + +Параметр `testsPerSession` отвечает за то, сколько тестов можно запускать последовательно в одной сессии браузера. Он ограничивает переиспользование сессии, чтобы не допустить падения тестов из-за деградации браузера, и не имеет отношения к параллельному запуску тестов. + +### Как это работает + +В рамках примера параметру `sessionsPerBrowser` присвоены значения 5: + +```typescript +chrome: { + headless: true, + desiredCapabilities: { + browserName: "chrome" + }, + sessionsPerBrowser: 5, // 5 параллельных сессий Chrome + waitTimeout: 10000, + } +``` + +и 2: + +```typescript +firefox: { + headless: true, + desiredCapabilities: { + browserName: "firefox" + }, + sessionsPerBrowser: 2, // 2 параллельные сессии Firefox + waitTimeout: 10000, + } +``` + +Значение параметра `workers`: + +```typescript +system: { + workers: 1, + }, +``` + +Далее тесты: + +```typescript +describe("test examples", () => { + it("Поиск элемента по data-testid", async ({ browser }) => { + // Открываем страницу и ждем ее загрузки + await browser.openAndWait("https://testplane.io/"); + + // Ищем элемент по атрибуту data-testid + const element = await browser.$('[data-testid="main-content"]'); + + // Проверяем существование элемента в DOM + const isExisting = await element.isExisting(); + console.log("Элемент с data-testid существует:", isExisting); + }); + + it("Поиск элемента на главной странице", async ({ browser }) => { + await browser.openAndWait("https://testplane.io/"); + + // Ищем элемент по классу "navbar" + const navbar = await browser.$(".navbar"); + + // Проверяем, отображается ли элемент на странице + const isDisplayed = await navbar.isDisplayed(); + console.log("Навбар отображается:", isDisplayed); + }); + + it("Поиск элемента по id на главной странице", async ({ browser }) => { + await browser.openAndWait("https://testplane.io/"); + + // Ищем элемент по id "__docusaurus" + const main = await browser.$("#__docusaurus"); + + // Проверяем, отображается ли элемент на странице + const isDisplayed = await main.isDisplayed(); + console.log("Элемент отображается:", isDisplayed); + }); + + it("Поиск элемента по типу атрибута", async ({ browser }) => { + await browser.openAndWait("https://testplane.io/"); + + // Ищем кнопку по атрибуту type="button" + // Формат селектора: element[type="value"] + const button = await browser.$('button[type="button"]'); + + // Проверяем существование элемента в DOM + const isExisting = await button.isExisting(); + console.log("Кнопка существует:", isExisting); + }); + + it("Поиск элемента по тексту", async ({ browser }) => { + await browser.openAndWait("https://testplane.io/"); + + // Ищем элемент по тексту внутри него + const link = await browser.$('//a[text()="Docs"]'); + + // Проверяем существование элемента в DOM + const isExisting = await link.isExisting(); + console.log("Элемент с текстом существует:", isExisting); + }); + + it("Поиск элемента по атрибуту", async ({ browser }) => { + await browser.openAndWait("https://testplane.io/"); + + // Ищем элемент по атрибуту type + const button = await browser.$('//button[@type="button"]'); + + // Проверяем существование элемента в DOM + const isExisting = await button.isExisting(); + console.log("Элемент с атрибутом существует:", isExisting); + }); + + it("Поиск кнопки с помощью метода getByRole", async ({ browser }) => { + await browser.url("https://testplane.io/"); + + const button = await browser.getByRole("button", { name: "Get started" }); + + await button.click(); + }); +}); +``` + +#### Как работает параллелизм в данном примере + +Для Chrome открывается до 5 окон браузера одновременно. Каждое из них — это отдельная независимая сессия со своим `sessionId`: + +```bash +Тест 1 → сессия chrome:abc123 → открывается окно Chrome #1 +Тест 2 → сессия chrome:def456 → открывается окно Chrome #2 +Тест 3 → сессия chrome:ghi789 → открывается окно Chrome #3 +Тест 4 → сессия chrome:jkl012 → открывается окно Chrome #4 +Тест 5 → сессия chrome:mno345 → открывается окно Chrome #5 +``` + +Для Firefox логика та же, но одновременно работают только 2 окна. Это ограничение задано через `sessionsPerBrowser`: + +```bash +Тест 1 → сессия firefox:fb59f7 → открывается окно Firefox #1 +Тест 2 → сессия firefox:660ee0 → открывается окно Firefox #2 +Тест 3 → ждёт в очереди... + как только тест 1 завершился → сессия fb59f7 освободилась +Тест 3 → сессия firefox:fb59f7 → то же окно Firefox #1, новый тест +``` + +:::warning Важно +`workers` управляет количеством `Node.js`-процессов, а `sessionsPerBrowser` — количеством одновременных браузерных сессий внутри каждого воркера. При `workers`: `1` все 7 сессий управляются одним процессом. +::: + +## Шардирование + +При наличии тысяч тестов время одного запуска может быть неприемлемо большим даже при максимальном параллелизме. В таких случаях используют шардирование — разбивку всего набора тестов на несколько независимых частей (чанков), которые запускаются параллельно на разных машинах или в разных `CI`-джобах. + +### Плагин testplane-chunks + +Плагин `@testplane/chunks` позволяет распараллелить запуск тестов на нескольких серверах, тем самым ускорив процесс. Однако сам плагин не занимается какой-либо оркестрацией, распараллеливанием запуска или слиянием получившихся отдельных отчетов в один итоговый отчет. + +#### Установка и подключение + +Для установки выполните команду: + +```bash +npm install -D @testplane/chunks +``` + +И укажите параметры в файле `testplane.config.ts`. + +```typescript +module.exports = { + plugins: { + "@testplane/chunks": { + count: 7, // Разбить тесты на 7 порций (чанков) + run: 1, // Запустить первую порцию + }, + + // другие плагины Testplane... + }, + + // другие настройки Testplane... +}; +``` + +#### Расшифровка параметров конфигурации + +| Параметр | Тип | По умолчанию | Описание | +| -------- | -------- | ------------ | ----------------------------------------------------------------- | +| `count` | `Number` | `1` | Количество порций (чанков), на которые нужно разбить набор тестов | +| `run` | `Number` | `1` | Номер чанка, тесты из которого нужно запустить | + +### Как разбить запуск тестов + +В качестве примера будет использован следующий набор тестов: + +```text +project/ +├── .testplane.conf.js +├── package.json +└── tests/ + ├── registration.testplane.js # 500 тестов + ├── payment.testplane.js # 500 тестов + └── auth.testplane.js # 500 тестов +``` + +```javascript +//registration.testplane.js + +describe("Регистрация", () => { + it("тест 1 - пустой email показывает ошибку", async ({ browser }) => {}); + it("тест 2 - невалидный формат email", async ({ browser }) => {}); + it("тест 3 - пустой пароль показывает ошибку", async ({ browser }) => {}); + + // ... + + // тест 500 +}); +``` + +```javascript +//payment.testplane.js + +describe("Оплата", () => { + it("тест 1 - пустой номер карты показывает ошибку", async ({ browser }) => {}); + it("тест 2 - невалидный номер карты", async ({ browser }) => {}); + it("тест 3 - истёкший срок действия карты", async ({ browser }) => {}); + + // ... + + // тест 500 +}); +``` + +```javascript +//auth.testplane.js + +describe("Авторизация", () => { + it("тест 1 - пустой email показывает ошибку", async ({ browser }) => {}); + it("тест 2 - пустой пароль показывает ошибку", async ({ browser }) => {}); + it("тест 3 - неверный пароль показывает ошибку", async ({ browser }) => {}); + + // ... + + // тест 500 +}); +``` + +Резделение тестов на чанки: + +```css +Чанк 0 → тесты 1, 2, ..., 500 (≈ первая треть) +Чанк 1 → тесты 1, 2, ..., 500 (≈ вторая треть) +Чанк 2 → тесты 1, 2, ..., 500 (≈ третья треть) +``` + +Запуск каждого чанка отдельно через терминал: + +```bash +# Терминал 1 — Чанк 0 +CHUNKS_COUNT=3 CHUNKS_CURRENT=0 npx testplane + +# Терминал 2 — Чанк 1 +CHUNKS_COUNT=3 CHUNKS_CURRENT=1 npx testplane + +# Терминал 2 — Чанк 2 +CHUNKS_COUNT=3 CHUNKS_CURRENT=2 npx testplane +``` + +### Как плагин делит тесты + +После сортировки по `fullTitle` набор тестов делится на 3 чанка: + +```css +┌──────────┬──────────┬──────────┐ +│ Чанк 0 │ Чанк 1 │ Чанк 2 │ +├──────────┼──────────┼──────────┤ +│ тест 1 │ тест 1 │ тест 1 │ +│ тест 2 │ тест 2 │ тест 2 │ +│ ... │ ... │ ... │ +│ тест 500 │ тест 500 │ тест 500 │ +└──────────┴──────────┴──────────┘ +``` + +:::tip Примечание +Главная идея чанков: если `workers` распределяет тесты внутри одного процесса, то чанки делят тесты между несколькими независимыми процессами — например, на разных `CI`-агентах или на одной машине через `concurrently`. +::: + +### Как объединить отчеты + +Чтобы объединить несколько отчетов в один, используйте команду `merge-reports`. Она принимает пути к директориям с отчетами, файлам баз данных или к файлам `databaseUrls.json`, после чего создает новый html-отчет в папке назначения с данными из всех переданных отчетов. + +Пример использования: + +```bash +npx html-reporter merge-reports report-dir/ path-to-database.db path-to-databaseUrls.json -d dest-report -h foo=bar +``` + +## Рекомендуемые настройки и их расчет + +#### Воркеры + +Рекомендуемое значение — 8. Любое другое не должно превышать количество ядер CPU. + +#### sessionsPerBrowser + +При локальном запуске значение не превышает 5. В ином случает примерное значение можно вычислить путем деления доступных ресурсов на требуемое для конкретного браузера. При расчете помните о затратах на запуск самих тестов. + +#### Количество чанков + +При расчете количества чанков необходимо отталкиваться от: + +- количества доступных браузеров +- оверхеда на создание чанка +- желаемого общего времени прогона + +Рекомендация — не меньше 500 тестов на чанк.