Avatar ·

Как отключить кэширование тестов в Go и принудительно перезапускать их?

Всем привет! Я начал изучать Go и пишу тесты для своего проекта. Заметил странное поведение: когда я запускаю тесты несколько раз подряд с помощью `go test`, после первого запуска они начинают выполняться мгновенно, даже если я изменил исходный код или тесты. В выводе я вижу `(cached)`, и это меня смущает. Пример: у меня есть простой тест: ```go package main import "testing" func TestAddition(t *testing.T) { result := 2 + 2 if result != 5 { // специально сделал ошибку t.Errorf("2 + 2 = %d, want 5", result) } } ``` Запускаю первый раз — тест падает, как и ожидалось. Исправляю `5` на `4`, запускаю снова — а тест всё равно показывает старый результат с ошибкой! Только после `go clean -testcache` или явного добавления флага `-count=1` тест запускается заново. Почему Go кэширует результаты тестов? Как правильно отключать это кэширование, чтобы тесты всегда выполнялись заново? Может быть, есть способ настроить это глобально или только для конкретных тестов? Заранее спасибо за помощь! P.S. Использую Go 1.21 на Windows 10. <<>>

Почему Go кэширует тесты?

Кэширование тестов — это полезная оптимизация, встроенная в Go начиная с версии 1.10. Когда вы запускаете go test, Go анализирует: исходный код теста, импортируемые пакеты, переменные окружения и флаги командной строки. Если ничего не изменилось с прошлого запуска, Go просто возвращает кэшированный результат, помеченный как (cached). Это экономит время, особенно в больших проектах.

Однако это может мешать, когда вы:

  • Тестируете код, который зависит от внешних ресурсов (файлы, БД, сеть).
  • Проверяете рандомизированное поведение.
  • Хотите убедиться, что тесты действительно проходят после изменений.

Как принудительно отключить кэширование?

Самый простой и рекомендуемый способ — использовать флаг -count=1. Он указывает Go выполнить тест ровно один раз, игнорируя кэш:

go test -count=1 ./...

Это работает для всех тестов в текущем пакете и его подпакетах. Флаг -count можно комбинировать с другими флагами, например:

go test -v -count=1 -run TestAddition ./...

Альтернативные методы

Если вы хотите очистить весь кэш тестов разово, используйте:

go clean -testcache

Это удалит все кэшированные результаты тестов, но не затронет кэш сборки. Для полной очистки всех кэшей Go:

go clean -cache

Как настроить кэширование глобально?

Go не предоставляет глобальной настройки для отключения кэширования тестов. Однако вы можете:

  • Создать алиас в shell: alias gotest='go test -count=1'
  • Использовать Makefile: test: go test -count=1 ./...
  • Настроить IDE (например, VS Code) добавлять флаг -count=1 в конфигурацию запуска тестов.

Когда кэширование не работает?

Go автоматически отключает кэширование, если:

  • Используется флаг -race (детектор гонок).
  • Задан флаг -bench или -benchmem.
  • Тест вызывает t.Parallel() (но не всегда — зависит от версии).
  • Переменная окружения GOFLAGS=-count=1 установлена.

Также кэш не применяется к тестам, которые используют testing.Short() или зависят от файлов вне модуля (через //go:embed).

Важные замечания

Не рекомендуется использовать go clean -testcache перед каждым запуском — это излишне и медленно. Флаг -count=1 — более элегантное решение. Также помните, что кэширование работает на уровне пакета: если вы изменили один файл в пакете, кэш для всего пакета инвалидируется автоматически.

Пример рабочего скрипта для CI/CD, который всегда запускает тесты свежими:

#!/bin/bash
set -e
echo "Running tests without cache..."
go test -count=1 -v ./...

Итог: используйте -count=1 для принудительного перезапуска тестов, а для разовой очистки — go clean -testcache.

<<>>

Дополнительный подход: флаг -count=1 как стандарт

Коллега верно описал основной метод. Хочу добавить, что в современных проектах часто используют переменную окружения GOFLAGS, чтобы не писать -count=1 каждый раз. Просто добавьте в свой .bashrc или .zshrc:

export GOFLAGS="-count=1"

Тогда любой вызов go test будет автоматически игнорировать кэш. Это удобно для разработки, но в CI/CD лучше явно указывать флаг, чтобы поведение было очевидным.

Другой вариант: инвалидация кэша через файлы

Если вам нужно, чтобы кэш сбрасывался при изменении определённых файлов (например, конфигов), можно использовать трюк с тестовыми флагами. Добавьте в тест чтение файла или переменной окружения:

func TestConfig(t *testing.T) {
    data, _ := os.ReadFile("config.yaml")
    // тест, зависящий от содержимого файла
}

Go автоматически отслеживает чтение файлов через os.ReadFile и инвалидирует кэш при их изменении. Но это работает только для файлов, указанных явно в коде теста.

Для бенчмарков

Если вы используете бенчмарки, кэширование отключается автоматически, но можно принудительно задать количество итераций:

go test -bench=. -benchtime=10x -count=1

Помните: кэширование — друг, а не враг. Не отключайте его без необходимости, особенно в больших проектах с сотнями тестов.