Skip to content
1 change: 1 addition & 0 deletions docs/basic-guides/parallelism.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Parallelism
Original file line number Diff line number Diff line change
@@ -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
});
```

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тут надо рассказать или сослаться на то, как объединить отчеты этих чанков в 1 с помощью команды merge-reports html-reporter.

Резделение тестов на чанки:

```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 тестов на чанк.
Loading