# Тесты на Go

{% note info %}

Пример проектов на Go можно найти [здесь](https://a.yandex-team.ru/arc/trunk/arcadia/devtools/dummy_arcadia/go).

{% endnote %}

Тесты на [Go](https://golang.org/) пишутся и располагаются в дереве исходных кодов ровно также, как это делается во внешнем мире. Для файла с исходным кодом `file.go` его тесты по соглашению располагаются в том же каталоге в файле `file_test.go`. Разрешено использование Go [модулей](https://golang.org/ref/mod) и файлов `go.mod`. Подробности о том, как пишутся файлы с тестами, можно почитать в официальной [документации](https://golang.org/pkg/testing/). Распространенной практикой является использование в коде тестов инструментов из библиотеки [testify](https://github.com/stretchr/testify).

## Проект с тестами { #test }

Типичный проект с тестами на Go имеет такой **ya.make** файл в директории `my/package`:

```yamake
OWNER(g:some-group)

GO_LIBRARY() # Или GO_PROGRAM()

SRCS( # Исходные коды
    file.go
)

GO_TEST_SRCS( # Исходные коды internal тестов
    file_test.go
)

END()
```

В go поддерживается два вида тестов - внутрениие (internal) и внешние (external).

* **Внутренние тесты** принадлежат непосредственно тестируемому пакету. Для описания файлов с таким кодом используется макрос `GO_TEST_SRCS` как в примере выше.
* **Внешние тесты** собираются в отдельный пакет, который импортирует тестируемый. Обычно в коде таких тестов вызываются обычные публичные функции тестируемого пакета. Для описания кода таких тестов используется макрос `GO_XTEST_SRCS`.

Поскольку в системе сборки ya make тесты не могут быть в одном модуле с библиотекой для модуля тестов заводится отдельная вложенная директория (обычно называемая gotest). В ней размещается специальный модульный
макрос `GO_TEST_FOR(path/to/package)` ссылающийся на пакет с кодом тестов. В нём же описываются обычные [тестовые макросы](./common.md). Тестовый модуль собирается в специальный пакет, интегрированный, поддержанный
на уровне интеграции go-тестов и ya make. Этот пакет позволяет запускать как внутренние, так и внешние тесты со стандартными возможностями тестирования в Аркадии - [листингом](../../usage/ya_make/tests.md#test_list), [фильтрацией](../../usage/ya_make/tests.md#test_filtering), [канонизацией](./canon.md), [рецептами](./recipe.md) и [прочими](../../usage/ya_make/tests.md).

**Пример:** в директории `my/package/gotest будет` такой ya.make

```yamake
GO_TEST_FOR(my/package)

OWNER(g:some-group)

SIZE(MEDIUM)          # Тест среднего размера
REQUIREMENTS(ram:24)  # Требует для работы 24GiB памяти

END()
```

{% note alert %}

В go пакеты для тестирования собираются иначе, чем для обычной линковки. В частности тестовая сборка может экспортировать допольнительное "тестовое API" для *внешних* тестов. При этом go на линковке проверяет, что во всех частях сборки финального артефакта один и тото же пакет был одинаковым.
Эти два факта создают следующую проблему: при раздельной компиляции пакеты не знают для какого артефакта они собираются и потому всегда собираются с "обычными" версиями своих зависимостей.
Тесты собираются с тестовой версией тестируемого модуля. Если в рамках теста на тестируемый модуль есть транзитивная засисимость, то возникает проблема несовпадения хэшей (checksum mismatch) у тестируемого модуля.

Это поведение - проблема дизайна go (решение для go 2.0 обсуждается [здесь](https://github.com/golang/go/issues/29258)).

На данный момент система ya make не только не умеет [собирать такое](https://st.yandex-team.ru/DEVTOOLS-5045), но даже [обнаруживать](https://st.yandex-team.ru/DEVTOOLS-9177).

Сборка такого рода представляет существенную проблему поскольку ya make допускает одновременную сборку нескольких целей в одном сборочном графе (в пределе - вообще всех целей в автосборке).
В этом случае каждый модуль может иметь транзитивные зависимости на множество других модулей для которых в графе есть тесты и для каждой такой зависимости (да, и транзитивной тоже) придётся строить
свой вариант модуля. Сборка всего и всегда в тестовом вариенте неприемлема, поскольку предоставляет тестовое API вообще всем потребителям. В результате хорошего решения этой задачи не существет.
Есть дизайн с явной разметкой всех потенциально-проблемных зависимостей во всех модулях, но он сложный как в разработке, так и в использовании и поддержке.

**На данный момент тесты с такой проблемой собрать средствами ya make не представляется возможным. Объезда этой проблемы кроме отказа от использования "тестового API" не существует.**

{% endnote %}


Для того, чтобы поддерживать файлы **ya.make** в актуальном состоянии, существует специальный инструмент [yo](https://a.yandex-team.ru/arc/trunk/arcadia/library/go/yo), автоматически обновляющий список файлов с исходными кодами и тестами в файле **ya.make**. После внесения изменений в проект достаточно выполнить одну команду, указав ей каталог с исходными кодами:

```bash
$ ya tool yo my-project/my-app/dir
```

Если `yo` у вас ещё не установлен, то он будет автоматически скачан при первом вызове команды.

## Проект с бенчмарками { #benchmark }

Кроме обычных тестов существует возможность запускать [бенчмарки](https://golang.org/pkg/testing/#hdr-Benchmarks):

```yamake
OWNER(g:some-group)

GO_TEST()

GO_TEST_SRCS(
    benchmark_test.go
)

TAG(ya:run_go_benchmark ya:manual)

END()
```

Код бенчмарков [пишется](https://a.yandex-team.ru/arc/trunk/arcadia/devtools/dummy_arcadia/go/go_benchmarks/) так, как показано в официальной документации Golang.


### To be documented

```
GO_TEST/GO_TEST_FOR
```
