Поддержка профилирования покрытия для интеграционных тестов
Содержание:
Обзор
Сборка двоичного файла для профилирования покрытия
Запуск двоичного файла с профилированием покрытия
Работа с файлами данных покрытия
Часто задаваемые вопросы
Ресурсы
Глоссарий
Начиная с Go 1.20, Go поддерживает сбор профилей покрытия из приложений и из интеграционных тестов, то есть более крупных и сложных тестов для программ на Go.
Обзор
Go предоставляет удобную поддержку сбора профилей покрытия на уровне модульных тестов с помощью команды “go
test -coverprofile=... <pkg_target>”.
Начиная с Go 1.20, пользователи теперь могут собирать профили покрытия для более крупных интеграционных тестов: более ресурсоемких и сложных тестов, которые
выполняют несколько запусков заданного двоичного файла приложения.
Для модульных тестов сбор профиля покрытия и генерация отчета требуют два шага: запуск go test
-coverprofile=..., за которым следует вызов go tool cover {-func,-html} для генерации
отчета.
Для интеграционных тестов необходимо три шага: сборка, запуск (который может включать несколько вызовов двоичного файла из шага сборки) и, наконец, генерация отчета, как описано ниже.
Сборка двоичного файла для профилирования покрытия
Чтобы собрать приложение для сбора профилей покрытия, передайте флаг -cover при вызове go
build для целевого двоичного файла приложения. См. раздел ниже для
примера вызова go build -cover.
Полученный двоичный файл затем можно запустить с использованием переменной окружения для захвата профилей
покрытия (см. следующий раздел о запуске).
Как выбираются пакеты для инструментирования
Во время вызова “go build -cover”, команда Go выберет пакеты в основном модуле для профилирования
покрытия; другие пакеты, участвующие в сборке (зависимости, указанные в go.mod, или пакеты из стандартной
библиотеки Go), по умолчанию не будут включены.
Например, вот игрушечная программа, содержащая главный пакет, локальный пакет модуля greetings и
набор пакетов, импортированных извне модуля, включая (среди прочего) rsc.io/quote и
fmt (ссылка на полную программу).
<code>$ cat go.mod
module mydomain.com
go 1.20
require rsc.io/quote v1.5.2
require (
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect
rsc.io/sampler v1.3.0 // indirect
)
$ cat myprogram.go
package main
import (
"fmt"
"mydomain.com/greetings"
"rsc.io/quote"
)
func main() {
fmt.Printf("I say %q and %q\n", quote.Hello(), greetings.Goodbye())
}
$ cat greetings/greetings.go
package greetings
func Goodbye() string {
return "see ya"
}
$ go build -cover -o myprogram.exe .
$
</code>
Если собрать эту программу с флагом командной строки “-cover” и запустить, то в профиль будут
включены ровно два пакета: main и mydomain.com/greetings; другие зависимые пакеты
будут исключены.
Пользователям, которые хотят иметь больше контроля над тем, какие пакеты включаются для покрытия, можно собирать
программы с флагом “-coverpkg”. Пример:
<code>$ go build -cover -o myprogramMorePkgs.exe -coverpkg=io,mydomain.com,rsc.io/quote . $ </code>
В приведённой выше сборке пакет из mydomain.com и пакеты rsc.io/quote и io
выбраны для профилирования; поскольку mydomain.com/greetings не указан явно, он будет исключён из
профиля, несмотря на то, что находится в основном модуле.
Запуск двоичного файла с инструментированным покрытием
Двоичные файлы, собранные с флагом “-cover”, записывают данные профиля в конце выполнения в
директорию, указанную через переменную окружения GOCOVERDIR. Пример:
<code>$ go build -cover -o myprogram.exe myprogram.go $ mkdir somedata $ GOCOVERDIR=somedata ./myprogram.exe I say "Hello, world." and "see ya" $ ls somedata covcounters.c6de772f99010ef5925877a7b05db4cc.2424989.1670252383678349347 covmeta.c6de772f99010ef5925877a7b05db4cc $ </code>
Обратите внимание на два файла, записанных в директорию somedata: эти (бинарные) файлы содержат
результаты покрытия. Подробнее о том, как получить человекочитаемые результаты из этих файлов данных, см. в
разделе о отчётах.
Если переменная окружения GOCOVERDIR не установлена, двоичный файл с инструментированным покрытием
всё равно будет выполняться корректно, но выведет предупреждение.
Пример:
<code>$ ./myprogram.exe warning: GOCOVERDIR not set, no coverage data emitted I say "Hello, world." and "see ya" $ </code>
Тесты, включающие несколько запусков
Интеграционные тесты могут включать в себя несколько запусков программы; когда программа собирается с флагом
“-cover”, каждый запуск создаёт новый файл данных. Пример
<code>$ mkdir somedata2 $ GOCOVERDIR=somedata2 ./myprogram.exe // первый запуск I say "Hello, world." and "see ya" $ GOCOVERDIR=somedata2 ./myprogram.exe -flag // второй запуск I say "Hello, world." and "see ya" $ ls somedata2 covcounters.890814fca98ac3a4d41b9bd2a7ec9f7f.2456041.1670259309405583534 covcounters.890814fca98ac3a4d41b9bd2a7ec9f7f.2456047.1670259309410891043 covmeta.890814fca98ac3a4d41b9bd2a7ec9f7f $ </code>
Файлы вывода данных покрытия имеют два вида: файлы метаданных (содержащие элементы, не изменяющиеся от запуска к запуску, такие как имена исходных файлов и функций), и файлы данных счётчиков (записывающие части программы, которые были выполнены).
В приведённом выше примере первый запуск создал два файла (счётчик и метаданные), тогда как второй запуск создал только файл данных счётчика: поскольку метаданные не изменяются от запуска к запуску, они записываются только один раз.
Работа с файлами данных покрытия
Go 1.20 вводит новый инструмент 'covdata', который может использоваться для чтения и манипуляции
файлами данных покрытия из директории GOCOVERDIR.
Инструмент covdata Go работает в различных режимах. Общий вид вызова инструмента
covdata выглядит следующим образом
<code>$ go tool covdata <mode> -i=<dir1,dir2,...> ...flags... </code>
где флаг “-i” предоставляет список директорий для чтения, каждая из которых получена при выполнении
двоичного файла с инструментированным покрытием (через GOCOVERDIR).
Создание отчётов профиля покрытия
В этом разделе рассматривается использование “go tool covdata” для создания человекочитаемых отчётов
из файлов данных покрытия.
Отчёт о проценте покрытых инструкций
Для получения метрики “процент покрытых инструкций” для каждого инструментированного пакета используйте команду “go
tool covdata percent -i=<directory>”.
Используя пример из раздела запуска
Проценты "покрытых инструкций" здесь соответствуют тем, которые сообщаются командой go test -cover.
Преобразование в устаревший текстовый формат
Вы можете преобразовать двоичные файлы данных покрытия в устаревший текстовый формат, создаваемый командой
“go test -coverprofile=<outfile>”, используя селектор textfmt в go tool
covdata. Полученный текстовый файл затем можно использовать с командами “go tool cover
-func” или “go tool cover -html” для создания дополнительных отчетов. Пример:
<code>$ ls somedata covcounters.c6de772f99010ef5925877a7b05db4cc.2424989.1670252383678349347 covmeta.c6de772f99010ef5925877a7b05db4cc $ go tool covdata textfmt -i=somedata -o profile.txt $ cat profile.txt mode: set mydomain.com/myprogram.go:10.13,12.2 1 1 mydomain.com/greetings/greetings.go:3.23,5.2 1 1 $ go tool cover -func=profile.txt mydomain.com/greetings/greetings.go:3: Goodbye 100.0% mydomain.com/myprogram.go:10: main 100.0% total: (statements) 100.0% $ </code>
Объединение
Подкоманда merge в “go tool covdata” может использоваться для объединения профилей из
нескольких каталогов данных.
Например, рассмотрим программу, которая запускается как на macOS, так и на Windows. Автор этой программы может захотеть объединить профили покрытия из отдельных запусков на каждой операционной системе в один профильный корпус, чтобы получить сводку покрытия кросс-платформенно. Пример:
<code>$ ls windows_datadir covcounters.f3833f80c91d8229544b25a855285890.1025623.1667481441036838252 covcounters.f3833f80c91d8229544b25a855285890.1025628.1667481441042785007 covmeta.f3833f80c91d8229544b25a855285890 $ ls macos_datadir covcounters.b245ad845b5068d116a4e25033b429fb.1025358.1667481440551734165 covcounters.b245ad845b5068d116a4e25033b429fb.1025364.1667481440557770197 covmeta.b245ad845b5068d116a4e25033b429fb $ ls macos_datadir $ mkdir merged $ go tool covdata merge -i=windows_datadir,macos_datadir -o merged $ </code>
Операция объединения выше объединит данные из указанных входных каталогов и запишет новый набор объединенных файлов данных в каталог “merged”.
Выбор пакетов
Большинство команд “go tool covdata” поддерживают флаг “-pkg” для выполнения выбора
пакетов как часть операции; аргумент флага “-pkg” принимает тот же формат, что и флаг “-coverpkg”
команды Go.
Пример:
<code> $ ls somedata covcounters.c6de772f99010ef5925877a7b05db4cc.2424989.1670252383678349347 covmeta.c6de772f99010ef5925877a7b05db4cc $ go tool covdata percent -i=somedata -pkg=mydomain.com/greetings mydomain.com/greetings coverage: 100.0% of statements $ go tool covdata percent -i=somedata -pkg=nonexistentpackage $ </code>
Флаг “-pkg” можно использовать для выбора конкретного подмножества интересующих пакетов для данного
отчета.
Часто задаваемые вопросы
- Как можно запросить инструментирование покрытия для всех импортированных пакетов,
упомянутых в файле
go.mod - Можно ли использовать
go build -coverв режиме GOPATH/GO111MODULE=off? - Если моя программа вызывает панику, будут ли записаны данные покрытия?
- Выберет ли
-coverpkg=mainмой главный пакет для профилирования?
Как можно запросить инструментирование покрытия для всех импортированных пакетов, упомянутых в
файле go.mod
По умолчанию go build -cover будет инструментировать все пакеты основного модуля для покрытия, но не
будет инструментировать импорты вне основного модуля
(например, пакеты стандартной библиотеки или импорты, перечисленные в go.mod).
Один из способов запросить инструментирование всех зависимостей, кроме стандартной библиотеки,
— это передать вывод go list в -coverpkg.
Вот пример, снова использующий
примерную программу, упомянутую выше:
<code>$ go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}' -deps . | paste -sd "," > pkgs.txt
$ go build -o myprogram.exe -coverpkg=`cat pkgs.txt` .
$ mkdir somedata
$ GOCOVERDIR=somedata ./myprogram.exe
$ go tool covdata percent -i=somedata
golang.org/x/text/internal/tag coverage: 78.4% of statements
golang.org/x/text/language coverage: 35.5% of statements
mydomain.com coverage: 100.0% of statements
mydomain.com/greetings coverage: 100.0% of statements
rsc.io/quote coverage: 25.0% of statements
rsc.io/sampler coverage: 86.7% of statements
$
</code>
Можно ли использовать go build -cover в режиме GO111MODULE=off?
Yes, go build -cover работает с GO111MODULE=off.
При сборке программы в режиме GO111MODULE=off только пакет, специально указанный в качестве цели в командной
строке, будет инструментирован для профилирования. Используйте флаг -coverpkg, чтобы включить
дополнительные пакеты в профиль.
Если моя программа паникует, будут ли данные о покрытии записаны?
Программы, собранные с помощью go build -cover, будут записывать полные данные профиля только в
конце выполнения, если программа вызывает os.Exit() или возвращает значение из
main.main нормально.
Если программа завершается неперехваченной паникой или сталкивается с фатальным исключением (например,
нарушением доступа к памяти, делением на ноль и т. д.), данные профиля из инструкций, выполненных во время
работы, будут потеряны.
Будет ли -coverpkg=main выбирать мой главный пакет для профилирования?
Флаг -coverpkg принимает список путей импорта, а не список имён пакетов. Если вы хотите выбрать ваш
main пакет для инструментирования покрытия, пожалуйста, укажите его по пути импорта, а не по имени.
Пример (используя этот пример программы):
<code>$ go list -m mydomain.com $ go build -coverpkg=main -o oops.exe . warning: no packages being built depend on matches for pattern main $ go build -coverpkg=mydomain.com -o myprogram.exe . $ mkdir somedata $ GOCOVERDIR=somedata ./myprogram.exe I say "Hello, world." and "see ya" $ go tool covdata percent -i=somedata mydomain.com coverage: 100.0% of statements $ </code>
Ресурсы
- Блог-пост, представляющий покрытие unit-тестов в Go 1.2:
- Профилирование покрытия для unit-тестов было представлено как часть выпуска Go 1.2; смотрите этот блог-пост для подробностей.
- Документация:
- Документация пакета
cmd/goописывает флаги сборки и тестирования, связанные с покрытием.
- Документация пакета
- Технические детали:
Глоссарий
unit-тест: Тесты внутри файла *_test.go, связанные с конкретным Go пакетом,
использующие Go пакет testing.
интеграционный тест: Более комплексный, тяжеловесный тест для заданного приложения или
бинарного файла. Интеграционные тесты обычно включают в себя сборку программы или набора программ, а затем
выполнение серии запусков программ с использованием множества входных данных и сценариев под контролем тестовой
среды, которая может быть основана на Go пакете testing или нет.