Разделение Интеграционных и Unit тестов в Go с помощью флага -short


В процессе создания приложения и написания тестов может возникнуть необходимость разделить тесты на две группы интеграционные тесты зависящие от окружения и unit тесты которые не зависят от окружения. Для таких ситуации рекомендую использовать флаг -short уже встроенный в go test -short ./..., во многих случаях этого простого деления будет достаточно, и более сложные механизмы вам не пригодятся.

Как это выглядит на практике:

  • go test -count=1 ./... — для запуска всех тестов.
  • go test -short ./... — для запуска только unit тестов.

Ещё для того чтобы этот подход заработал корректно во всех интеграционных тестах нужно будет добавить конструкцию вида if testing.Short() {}:

  func TestJustDoItCommandHandler_Handle(t *testing.T) {
    if testing.Short() {
      t.Skip("skipping test in short mode")
    }
    
    // ...
  }

В Makefile такой подход может выглядеть следующим образом:

  • make test — для запуска всех тестов.
  • make test-short — для запуска только unit тестов.
.PHONY: test
test: ## Run all tests.
	@go test -count=1 ./...

.PHONY: test-short
test-short: ## Run only unit tests, tests without I/O dependencies.
	@go test -short ./...

Так, подождите, а зачем вообще -count=1? — Этот флаг нужен, чтобы очищать кеш тестов перед прогоном интеграционных тестов, да если вы вдруг не знали Go кеширует результаты тестов, что весьма не плохо для unit-тестов, но для интеграционных тестов это может стать проблемой потому что результаты их зависят не только от изменений в коде, но и от изменений в окружении.

Для сброса кеша тестов в Go идиоматичным считается использование флага -count=1, альтернативой этому служит команда go clean -testcache. Подробнее можно ознакомиться с описанием воспользовавшись командами go help testflag и go help clean.

Альтернативы

  • Build tags (1,2) - При таком подходе в файлах с тестами указывает тег сборки например integration и такие тесты запускаются командой go test -tags=integration. У этого подхода есть множество проблем с тем что, линтеры начинают пропускать такие файлы, IDE могут не отображать корректно код или не запускать тесты без добавления тега сборки, и так далее.
  • Environment variables (3) - Способ похож на использование флага -short но только условный оператор уже ориентируется на переменную окружения if os.Getenv("...") == "" {}.

Глоссарий

  • Интеграционный тест – в рамках этой статьи таким тестом буду называть любой тест работающий с I/O, на практике это чаще всего тесты которые требуют наличия в окружении определенной зависимости, базы данных, SMTP сервера, очередь сообщений, специфическую FS если вдруг наш код завязан на тонкости определенной файлов системы, и так далее. Но также допускаю что если для работы теста нужно запустить http.Server{} прямо из кода теста и этот тест не зависит от окружения, то не обязательно помечать его как интеграционный.
  • Unit тест - определение для таких тестов может тоже сильно разниться, в данной статье это любые тесты которые не зависят от окружения и условно запускаются и проходят на “всех” компьютерах.