Я люблю заглядывать в тесты, чтобы быстро понять, как работает код. Например, тесты в fmt-пакете объясняют работу ряда модификаторов нагляднее, чем документация.
Также я обращаю внимание на пакет *_test.go
файла. Если этот тот же пакет, что и тестируемый, то
это модульное
тестирование (есть доступ к неэкспортированные переменным). Если же это другой пакет, то часто это
интеграционное
тестирование, либо "black box" тестирование, т.е. такое, в котором проверяется работа нескольких функций/пакетов
вместе.
Также можно выполнение тестов пропускать, используя метод Skip()
. Запуск тестов поддерживает
переменную,
позволяющую просигнализировать, что тесты должны быть короткими, для чего тесты следует запускать с флагом
-short
,
а в тестах проверять значение этой переменной вызовом testing.Short()
.
Если нужно выполнить код, до и после выполнения тестов, то удобно использовать TestMain
функцию:
func TestMain(m *testing.M) { // setup os.Exit(m.Run()) // teardown }
Организацию по типу табличных тестов можно сделать и с использованием суб-тестов t.Run()
.
Удобны для демонстрации вашей идеи. Из полезного стоит отметить, что Examples-функции можно публиковать в Go playground'е (как и обычные тесты, но не бенчмарки), чтобы продемонстрировать работу вашего кода и сбросить например коллеге в чат ссылку на него.
Прервать выполнение теста можно с помощью t.Fatal(f) и t.Error(f). Лучше использовать t.Error(f), чтобы выявить за один тест как можно больше ошибок и только если дальнеший тест невозможен из-за текущего состояния системы, то использовать t.Fatal(f).
По-умолчанию бэнчмарки не запускаются при выполнении go test
. Нужно явно указать флаг
-bench
.
Причем, если не указать название бенчмарка, то будут запущены все бенчмарки в пакете. Выполняются они
последовательно.
Бенчмарк по итогу работы выводит на экран скорость выполнения тестируемого кода и сколько было мемори-аллокаций
(дополнительный флаг -benchmem
).
Т.к. бенчмарки на разных машинах дают разные результаты, то ценен именно сравнительный анализ (бенчмарки не нужно использовать для проверки скорости работы кода, для этого лучше подойдет QA-тестирование) тестируемого кода, выполняемый на одной машине. Т.е. тестируем старую версию кода и новую и сравниваем результаты. Для удобного сравнения двух результатов можно использовать утилиту benchstat. Она показывает процентное изменение результатов.
Появилось в Go 1.18.
Часто используется для тестирования парсеров и энкодеров/декодеров. Применяется для тестирования быстрого простого кода без разделяемого стейта (shared state).
В основе фаззи-тестов сравнение стак-трейсов. Генерируются значения, сравниваются отклонения от предыдущих стак-трейсов. Фаззи-тест никогда не может быть пройден, если самостоятельно его не ограничить.
Документация: https://go.dev/security/fuzz/
Подробный туториал: https://go.dev/doc/tutorial/fuzz
Механизм кеширования, введенный в Go 1.10, сильно ускорил выполнение тестов, исходный код которых не был изменен.
Вы точно обратили внимание как быстро выполняются тесты, особенно, если это второй и последующие запуски. Дело в
том, что билды и результаты выполнения тестов кешируются в директории $GOCACHE
или в домашней
директории пользователя $HOME/.cache/go-build
.
При выполнении тестов, если исходный код не изменялся, то тесты выполняются на основе кеша, о чем сообщается
коментарием (cached)
в консоли рядом с тестом.
Очистка кеша возможно командой go clean -cache
.
Добавлено в Go 1.16 для мока файловой системы в тестах.
Часто приходится работать с файловой системой в коде. В этом случае в качестве зависимости лучше указать
'io/fs.FS'
(интерфейс с единственным методом Open()
, возвращает файл и ошибку).
Хорошая демонстрация использования: https://quii.gitbook.io/learn-go-with-tests/go-fundamentals/reading-files
Также здесь же есть удобная хелпер-функции TestFS(fsys fs.FS, expected ...string)
для проверки
наличия
файлов в указанной файловой системе
Если в вашем коде есть функции/методы, которые принимаю Reader` или `Writer
интерфейсы, то вы
можете
протестировать их с помощью iotest` пакета. Он содержит реализации `Reader` и `Writer
интерфейсов,
для
некоторых корнер-кейсов: ErrReader`, `HalfReader`, `OneByteReader`, `TimeoutReader
и т.д.
go test -p 1
- запускает тесты последовательно, тестируется один пакет за раз. В противном случае
запускаются
Вызов t.Parallel()
сигнализирует testing-фреймворку, что тест можно запускать конкурентно с другими
тестами, в
которых есть вызов этой же функции. Если же возникли проблемы с конкурентным выполнением тестов, то можно
использовать флаг -parallel N
для ограничения количества параллельно выполняющихся рутин.
Суб-тесты выполняются последовательно, поэтому если нужно их запустить параллельно, то нужно делать вызов
t.Parallel()
в каждом из них.
Покрытие кода тестами проверить очень легко - просто добавить флаг -cover` при запуске `go test
. Мы
увидим
проценты покрытия по каждому протестированному пакету.
Можно пойти дальше, сформировать профайл покрытия кода тестами: go test -coverprofile=coverage.out
./...
(на
выходе простой файл с перечислением позиций в коде, покрытых тестом или нет) и после этого визуализировать его с
помощью утилиты go tool cover -html=coverage.out
.
Покрытие кода в первую очередь используем для идентификации непротестированных областей.
Если у вас большая команда, то часто покрытие кода тестами включают в CI-цикл.