Разделение модульных тестов и интеграционных тестов в Go
Существует ли наилучшая практика для разделения модульных тестов и тестов интеграции в GoLang (свидетельствовать)? У меня есть сочетание модульных тестов (которые не полагаются на какие-либо внешние ресурсы и, следовательно, работают очень быстро) и интеграционные тесты (которые полагаются на любые внешние ресурсы и тем самым работают медленнее). Итак, я хочу иметь возможность контролировать, включать ли интеграционные тесты, когда я говорю go test
.
Наиболее простой метод, казалось бы, заключается в определении флага -integrate в main:
var runIntegrationTests = flag.Bool("integration", false
, "Run the integration tests (in addition to the unit tests)")
И затем добавить инструкцию if в начало каждого теста интеграции:
if !*runIntegrationTests {
this.T().Skip("To run this test, use: go test -integration")
}
Это лучшее, что я могу сделать? Я искал документацию для показа, чтобы узнать, существует ли, возможно, соглашение об именах или что-то, что это делает для меня, но ничего не нашел. Я что-то пропустил?
Ответы
Ответ 1
@Ainar-G предлагает несколько отличных шаблонов для разделения тестов.
Этот набор методов Go из SoundCloud рекомендует использовать теги сборки (описанные в разделе "Строить ограничения" в сборке пакет), чтобы выбрать, какие тесты следует запускать:
Напишите Integration_test.go и дайте ему тег построения интеграции. Определите (глобальные) флаги для таких вещей, как служебные адреса и соединения строк, и используйте их в своих тестах.
// +build integration
var fooAddr = flag.String(...)
func TestToo(t *testing.T) {
f, err := foo.Connect(*fooAddr)
// ...
}
Тест go принимает теги сборки, подобные go build, поэтому вы можете вызвать go test -tags=integration
. Он также синтезирует основной пакет, который вызывает flag.Parse, поэтому любые объявленные и видимые флаги будут обрабатываться и доступны для ваших тестов.
В качестве аналогичного варианта вы также можете выполнить тесты интеграции по умолчанию, используя условие построения // +build !unit
, а затем отключите их по запросу, запустив go test -tags=unit
Ответ 2
Я вижу три возможных решения. Первый - использовать короткий режим для модульных тестов. Таким образом, вы использовали бы go test -short
с модульными тестами и те же, но без флага -short
, чтобы запустить ваши интеграционные тесты. Стандартная библиотека использует короткий режим, чтобы либо пропускать длительные тесты, либо заставлять их работать быстрее, предоставляя более простые данные.
Вторым является использование соглашения и вызов ваших тестов либо TestUnitFoo
, либо TestIntegrationFoo
, а затем используйте -run
флаг тестирования, чтобы обозначить, какие тесты для запустить. Таким образом, вы должны использовать go test -run 'Unit'
для модульных тестов и go test -run 'Integration'
для тестов интеграции.
Третий вариант - использовать переменную окружения и получить ее в настройках тестов с помощью os.Getenv
. Затем вы должны использовать простые go test
для модульных тестов и FOO_TEST_INTEGRATION=true go test
для тестов интеграции.
Я лично предпочел бы решение -short
, так как оно проще и используется в стандартной библиотеке, поэтому он, по-видимому, является де-факто способом разделения/упрощения длительных тестов. Но решения -run
и os.Getenv
предлагают большую гибкость (требуется также более осторожно, поскольку регулярные выражения связаны с -run
).
Ответ 3
Чтобы подробно описать мой комментарий к замечательному ответу @Ainar-G, за последний год я использовал комбинацию -short
с соглашением об именах Integration
для достижения наилучшего из обоих миров.
Единица измерения и интеграция проверяют гармонию в том же файле
Флаги сборки ранее заставляли меня иметь несколько файлов (services_test.go
, services_integration_test.go
и т.д.).
Вместо этого возьмите этот пример ниже, где первые два являются модульными тестами, и у меня есть тест интеграции в конце:
package services
import "testing"
func TestServiceFunc(t *testing.T) {
t.Parallel()
...
}
func TestInvalidServiceFunc3(t *testing.T) {
t.Parallel()
...
}
func TestPostgresVersionIntegration(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
...
}
Обратите внимание, что в последнем тесте есть соглашение:
- используя
Integration
в имени теста.
- проверка, если выполняется под
-short
директивой флага.
В принципе, спецификация идет: "Обычно пишите все тесты, если это длительные тесты или тест интеграции, следуйте этому соглашению об именах и проверьте, чтобы -short
был приятным для ваших сверстников".
Выполнять только Единичные тесты:
go test -v -short
это дает вам приятный набор сообщений, например:
=== RUN TestPostgresVersionIntegration
--- SKIP: TestPostgresVersionIntegration (0.00s)
service_test.go:138: skipping integration test
Только теги интеграции только:
go test -run Integration
Выполняются только тесты интеграции. Полезно для дымообразующих канарейков в производстве.
Очевидно, недостатком этого подхода является то, что если кто-то запускает go test
, без флага -short
, он будет по умолчанию запускать все тесты - тесты на единицу и интеграцию.
В действительности, если ваш проект достаточно велик для проведения тестов на единицу и интеграцию, вы, скорее всего, используете Makefile
, где вы можете иметь простые директивы для использования go test -short
в нем. Или просто поместите его в свой README.md
файл и назовите его днем.
Ответ 4
Пример из SoundCloud docs выше имеет несчастливое свойство, которое "теги" интерпретируется как тег сборки и не будет передаваться тестовый код. Используйте другое имя флага, если вы хотите следовать этому подходу. Мне потребовалось немного, чтобы понять, что cmd/go/{build, testflag}.go определяет и интерпретирует flags
.
Флаги go test определены в: