---
metadata:
  - name: generator
    content: Diplodoc Platform v5.63.3
alternate:
  - https://ytsaurus.tech/docs/en/flow/cpp/testing.md
  - https://ytsaurus.tech/docs/ru/flow/cpp/testing.md
  - href: https://ytsaurus.tech/docs/ru/flow/cpp/testing.md
    type: text/markdown
    title: Markdown version
  - href: https://ytsaurus.tech/docs/ru/llms.txt?revision=181542d8c2d9f36be925894a517db8203cafae65
    rel: describedby
---
> **Documentation Index:** Fetch the complete configuration index at https://ytsaurus.tech/docs/ru/llms.txt?revision=181542d8c2d9f36be925894a517db8203cafae65

<!-- source: ru/_includes/flow/cpp/testing.md -->
# Тестирование в YTsaurus Flow (C++)

{% note info %}

C++ [воркеры](https://ytsaurus.tech/docs/ru/flow/concepts/glossary.md#worker) на данный момент не имеют отдельного фреймворка для юнит-тестирования компьютейшенов: основной подход — запускать пайплайн целиком с конечными источниками данных. Пример теста можно найти в [examples/cpp/wait_click_join](https://github.com/ytsaurus/ytsaurus/tree/main/yt/yt/flow/examples/cpp/wait_click_join). Если писать юнит тесты всё же необходимо, то можно выносить бизнес-логику из компьютейшнов в отдельные классы и писать юнит тесты уже на них.

{% endnote %}

<!-- source: ru/_includes/flow/testing-integration-body.md -->
Общие принципы написания интеграционных тестов на пайплайны:
* Тестируем конечные сценарии. То есть:
  * Записываем входные/исходные данные в локальный YT.
  * В спеке пайплайна отмечаем источники как `finite=%true`.
  * Для [Key-visitor-стрима](https://ytsaurus.tech/docs/ru/flow/concepts/key_visitor.md), который должен работать во время теста, задаём `finite=%false`.
  * Запускаем пайплайн.
  * Когда визитор должен завершиться, вызываем метод базового класса интеграционного теста [`self.ask_key_visitor_to_complete("<computation_id>", "<stream_id>")`](https://github.com/ytsaurus/ytsaurus/tree/main/yt/yt/flow/library/python/integration_test_base/yt_flow_base.py): он переключает [динамический параметр `finite`](https://ytsaurus.tech/docs/ru/flow/concepts/key_visitor.md#dynamic-params) этого стрима на `%true`.
  * Ждём, пока пайплайн завершится.
  * Проверяем выходные данные.
* Готовим окружение тем же кодом, что и в проде:
  * Тестируем тот же бинарь пайплайна что будет работать в проде.
  * Генерируем спеки пайплайна тем же кодом, что будет генерировать их для прода.
  * Если входные/выходные данные нетривиально сериализуются/парсятся, то делаем эту работу общим с продом кодом.
* Используем [общий тестовый фреймворк](https://github.com/ytsaurus/ytsaurus/tree/main/yt/yt/flow/library/python/integration_test_base) (есть `README.md`).
* Делаем failover тест.
  * Многие ошибки вскрываются на выпадении воркеров и переподхвате их работы другими воркерами. Поэтому делаем тест с `problems=True` и с более чем 1 воркером.
* Пишем стабильные тесты.
  * Помним, что в CI любая часть теста может работать неожиданно долго.
    * Идеальное время работы одного теста &mdash; 20 секунд при любом типе сборки.
    * Для санитайзерных сборок уменьшаем количество входных данных.
    * Все локальные таймауты выставляем с кратным запасом.
  * Логика выполнения теста не должна значимо зависеть от текущего времени. Например, тест не должен падать, если запустился до полуночи, а завершился &mdash; после.


## Отладка тестов {#debug}

### Логи {#debug-logs}

После завершения работы теста его логи можно найти в `test-results/py3test/testing_out_stuff` в директории теста. Основные важные:
* `run.log` &mdash; логи python-теста.
* `<test_class_name>/<test_name>/Controller_<number>...` &mdash; логи контроллеров (`.err` &mdash; это stderr процесса, `.log` &mdash; обычные логи, которые пишутся через `YT_LOG_...`).
* `<test_class_name>/<test_name>/Worker_<number>...` &mdash; логи воркеров.
* `<test_class_name>/<test_name>/Runner...` &mdash; логи раннера.

Если что-то не работает, то имеет смысл поискать ошибки во всех этих логах. Принцип изучения логов такой же, как в рабочем пайплайне; см. [логи Vanilla-операции](https://ytsaurus.tech/docs/ru/flow/devops/vanilla/diagnostics/logs.md).

Логи можно посмотреть ещё до завершения теста, для этого нужно найти временную директорию, в которой тест работает. Самый простой способ — запускать тест с флагом `--keep-temps`: `ya make --keep-temps -ttt <target>`. В этом случае `ya make` не удалит временную директорию по окончании теста и напечатает ссылку на неё в выводе.

Также можно воспользоваться такими bash-алиасами:

```bash
alias curtestdir="ps -f -u $USER | python3 -c \"import sys, re; drs = set(e for e in re.findall(r'[\s=](/\S*testing' + r'_out_stuff)\b', sys.stdin.read())); print('' if len(drs) == 1 else 'Select first from ' + repr(drs), file=sys.stderr); print(list(drs)[0])\""
alias cdcurtestdir='cd $(curtestdir)'

# Достать из логов ссылку на UI локального YTsaurus.
alias curlocalyt='cat $(curtestdir)/stderr 2>/dev/null | grep YT'
```


### Тестовый фреймворк {#debug-test-framework}

Как настроить окружение для лучшей работы тестового фреймворка и как можно влиять на параметры тестирования можно прочитать в [README.md](https://github.com/ytsaurus/ytsaurus/tree/main/yt/yt/flow/library/python/integration_test_base) фреймворка.
<!-- endsource: ru/_includes/flow/testing-integration-body.md -->

<!-- source: ru/_includes/flow/testing-test-param-body.md -->
Поведение интеграционных тестов настраивается через `--test-param NAME=VAL`:

#|
|| **Параметр** | **По умолчанию** | **Значения** | **Действие** ||
|| `RUNNER_LOG_LEVEL` | | `Error`, `Info`, `Debug`, … | Уровень логирования процесса, запускающего пайплайн (runner). ||
|| `PAUSE_BEFORE_FLOW_PROCESS_FEDERATION_TEARDOWN` | `0` | `0`, `1` | Зависнуть в тесте перед остановкой процессов Flow. В комбинации с `--test-disable-timeout` позволяет надолго оставить работающий локальный YTsaurus и федерацию процессов Flow для неспешного изучения через UI. ||
|| `EXTERNAL_YT_CONFIG` | (не задан) | yson — см. ниже | Запускать пайплайн на реальных внешних кластерах YTsaurus вместо локального рецепта. ||
|#

Примеры:

```bash
ya make -A --test-param RUNNER_LOG_LEVEL=Debug
ya make -A --test-disable-timeout --test-param PAUSE_BEFORE_FLOW_PROCESS_FEDERATION_TEARDOWN=1
```

### `EXTERNAL_YT_CONFIG` {#external-yt-config}

Локальный YTsaurus всё ещё стартует рецептом, но тест его игнорирует.

Обязательные поля, общие для всех кластеров:

- `path` — базовая директория.
- `tablet_cell_bundle` — bundle создаваемых динамических таблиц.
- `proxy_role` — RPC proxy role.

Опционально: `primary_medium` (по умолчанию `"default"`).

Список `clusters` — первый элемент primary. Поля записи: `cluster_name` (обязательно), `proxy_url` (по умолчанию равен `cluster_name`).

Авторизация: при внешнем YTsaurus `YT_TOKEN`/`YT_USER` из локального рецепта чистятся, yt-wrapper подхватывает токен из `~/.yt/token`.

Изоляция: `work_yt_path = path/<local-username>/<test_name>`; директория `path/<username>` удаляется и пересоздаётся один раз на класс в `setup_class`.

Пример:

```bash
ya make -A --test-param 'EXTERNAL_YT_CONFIG={path="//tmp/yt_flow";tablet_cell_bundle=default;proxy_role=default;clusters=[{cluster_name=<cluster-name>};];}'
```
<!-- endsource: ru/_includes/flow/testing-test-param-body.md -->

## См. также

- [Базовые правила выкатки](https://ytsaurus.tech/docs/ru/flow/devops/vanilla/releases.md#release-and-configure-basic-rules)
- [Тестирование (Java)](https://ytsaurus.tech/docs/ru/flow/java/testing.md)
- [Тестирование (Python)](https://ytsaurus.tech/docs/ru/flow/python/testing.md)
- [Быстрый старт (C++)](https://ytsaurus.tech/docs/ru/flow/cpp/getting-started.md)
- [Быстрый старт (Java)](https://ytsaurus.tech/docs/ru/flow/java/getting-started.md)
- [Быстрый старт (Python)](https://ytsaurus.tech/docs/ru/flow/python/getting-started.md)
<!-- endsource: ru/_includes/flow/cpp/testing.md -->
