-
Notifications
You must be signed in to change notification settings - Fork 5
docs: parallelism #120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Nikolaengel
wants to merge
13
commits into
master
Choose a base branch
from
docs-parallelism
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+372
−0
Open
docs: parallelism #120
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
578c847
docs: parallelism-add
Nikolaengel fa7c1be
docs: perallelism-fix
Nikolaengel 7440dd5
docs: character-fix
Nikolaengel c98a6c5
docs: character-fix
Nikolaengel 4468c11
docs: character-fix
Nikolaengel c0370dc
docs: settings-fix
Nikolaengel 9e6ef78
docs: sessions-fix
Nikolaengel f96dad9
docs: character-fix
Nikolaengel d47f5ce
docs: character-fix
Nikolaengel 80ec6b9
docs: reports-add
Nikolaengel 7cb70b5
docs: chunks-fix
Nikolaengel 7817151
docs: character-fix
Nikolaengel d1eaf7f
docs: chunks-fix
Nikolaengel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Parallelism |
371 changes: 371 additions & 0 deletions
371
i18n/ru/docusaurus-plugin-content-docs/current/basic-guides/parallelism.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,371 @@ | ||
| # Параллельный запуск тестов | ||
|
|
||
| ## Параллельность в 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` | Номер чанка, тесты из которого нужно запустить | | ||
|
|
||
| ### Как разбить запуск тестов | ||
|
|
||
| В качестве примера будет использован следующий набор тестов: | ||
|
|
||
| ```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(); | ||
| }); | ||
| }); | ||
| ``` | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Тут надо рассказать или сослаться на то, как объединить отчеты этих чанков в 1 с помощью команды merge-reports html-reporter. |
||
| Резделение тестов на чанки: | ||
|
|
||
| ```css | ||
| Чанк 0 → тесты 1, 2, 3 (≈ первая треть) | ||
| Чанк 1 → тесты 4, 5 (≈ вторая треть) | ||
| Чанк 2 → тесты 6, 7 (≈ третья треть) | ||
| ``` | ||
|
|
||
| Запуск каждого чанка отдельно через терминал: | ||
|
|
||
| ```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 │ тест 4 │ тест 6 │ | ||
| │ тест 2 │ тест 5 │ тест 7 │ | ||
| │ тест 3 │ │ │ | ||
| └──────────┴──────────┴──────────┘ | ||
| ``` | ||
|
|
||
| :::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`. В ином случает примерное значение можно вычислить путем деления доступных ресурсов на требуемое для конкретного браузера. При расчете помните о затратах на запуск самих тестов. | ||
|
|
||
| #### Количество чанков | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Исходя из этой диаграммы складывается ощущение, что главный инструмент управление параллельностью — воркеры. Это не так. Воркер может быть 1 и он может управляеть большим количеством браузеров одновременно. Главный параметр — это sessionsPerBrowser.
Диаграмму надо убрать или переработать.