`go test` の33個のフラグを全て解説してみた!!! (サンプルコード付き)
導入
みなさん、Goでテストを実行したことはありますか?
その時には go test
コマンドを実行することがあると思います。
実は go test
コマンドには30個以上のフラグが用意されているのをご存知でしょうか??
(ちなみに筆者はそのほとんどを知らなかったです)
このブログでは、そのすべてのフラグの使い方を説明しようと思います!!
とても長いので目次から気になるフラグだけピックアップしてみると読みやすいかと思います!
あるいは「❤️」していただいて備忘録的に見返してもらうのも良いかと思います!
Goのバージョンは以下を参考にしてください。
❯ go version
go version go1.23.1 darwin/arm64
またこのブログでは testing
パッケージの使い方やpackageの指定の仕方などに関して、詳細な説明はしません。
フラグを全部見てみる
とりあえずすべてのフラグを見てみます!
以下のコマンドを実行すれば、現時点 (2024/10) で go test
が受け付けるすべてのフラグが分かります。
❯ go help testflag
結果は長いのでトグルにしてます。
`go help testflag` の実行結果
The 'go test' command takes both flags that apply to 'go test' itself
and flags that apply to the resulting test binary.
Several of the flags control profiling and write an execution profile
suitable for "go tool pprof"; run "go tool pprof -h" for more
information. The --alloc_space, --alloc_objects, and --show_bytes
options of pprof control how the information is presented.
The following flags are recognized by the 'go test' command and
control the execution of any test:
-bench regexp
Run only those benchmarks matching a regular expression.
By default, no benchmarks are run.
To run all benchmarks, use '-bench .' or '-bench=.'.
The regular expression is split by unbracketed slash (/)
characters into a sequence of regular expressions, and each
part of a benchmark's identifier must match the corresponding
element in the sequence, if any. Possible parents of matches
are run with b.N=1 to identify sub-benchmarks. For example,
given -bench=X/Y, top-level benchmarks matching X are run
with b.N=1 to find any sub-benchmarks matching Y, which are
then run in full.
-benchtime t
Run enough iterations of each benchmark to take t, specified
as a time.Duration (for example, -benchtime 1h30s).
The default is 1 second (1s).
The special syntax Nx means to run the benchmark N times
(for example, -benchtime 100x).
-count n
Run each test, benchmark, and fuzz seed n times (default 1).
If -cpu is set, run n times for each GOMAXPROCS value.
Examples are always run once. -count does not apply to
fuzz tests matched by -fuzz.
-cover
Enable coverage analysis.
Note that because coverage works by annotating the source
code before compilation, compilation and test failures with
coverage enabled may report line numbers that don't correspond
to the original sources.
-covermode set,count,atomic
Set the mode for coverage analysis for the package[s]
being tested. The default is "set" unless -race is enabled,
in which case it is "atomic".
The values:
set: bool: does this statement run?
count: int: how many times does this statement run?
atomic: int: count, but correct in multithreaded tests;
significantly more expensive.
Sets -cover.
-coverpkg pattern1,pattern2,pattern3
Apply coverage analysis in each test to packages matching the patterns.
The default is for each test to analyze only the package being tested.
See 'go help packages' for a description of package patterns.
Sets -cover.
-cpu 1,2,4
Specify a list of GOMAXPROCS values for which the tests, benchmarks or
fuzz tests should be executed. The default is the current value
of GOMAXPROCS. -cpu does not apply to fuzz tests matched by -fuzz.
-failfast
Do not start new tests after the first test failure.
-fullpath
Show full file names in the error messages.
-fuzz regexp
Run the fuzz test matching the regular expression. When specified,
the command line argument must match exactly one package within the
main module, and regexp must match exactly one fuzz test within
that package. Fuzzing will occur after tests, benchmarks, seed corpora
of other fuzz tests, and examples have completed. See the Fuzzing
section of the testing package documentation for details.
-fuzztime t
Run enough iterations of the fuzz target during fuzzing to take t,
specified as a time.Duration (for example, -fuzztime 1h30s).
The default is to run forever.
The special syntax Nx means to run the fuzz target N times
(for example, -fuzztime 1000x).
-fuzzminimizetime t
Run enough iterations of the fuzz target during each minimization
attempt to take t, as specified as a time.Duration (for example,
-fuzzminimizetime 30s).
The default is 60s.
The special syntax Nx means to run the fuzz target N times
(for example, -fuzzminimizetime 100x).
-json
Log verbose output and test results in JSON. This presents the
same information as the -v flag in a machine-readable format.
-list regexp
List tests, benchmarks, fuzz tests, or examples matching the regular
expression. No tests, benchmarks, fuzz tests, or examples will be run.
This will only list top-level tests. No subtest or subbenchmarks will be
shown.
-parallel n
Allow parallel execution of test functions that call t.Parallel, and
fuzz targets that call t.Parallel when running the seed corpus.
The value of this flag is the maximum number of tests to run
simultaneously.
While fuzzing, the value of this flag is the maximum number of
subprocesses that may call the fuzz function simultaneously, regardless of
whether T.Parallel is called.
By default, -parallel is set to the value of GOMAXPROCS.
Setting -parallel to values higher than GOMAXPROCS may cause degraded
performance due to CPU contention, especially when fuzzing.
Note that -parallel only applies within a single test binary.
The 'go test' command may run tests for different packages
in parallel as well, according to the setting of the -p flag
(see 'go help build').
-run regexp
Run only those tests, examples, and fuzz tests matching the regular
expression. For tests, the regular expression is split by unbracketed
slash (/) characters into a sequence of regular expressions, and each
part of a test's identifier must match the corresponding element in
the sequence, if any. Note that possible parents of matches are
run too, so that -run=X/Y matches and runs and reports the result
of all tests matching X, even those without sub-tests matching Y,
because it must run them to look for those sub-tests.
See also -skip.
-short
Tell long-running tests to shorten their run time.
It is off by default but set during all.bash so that installing
the Go tree can run a sanity check but not spend time running
exhaustive tests.
-shuffle off,on,N
Randomize the execution order of tests and benchmarks.
It is off by default. If -shuffle is set to on, then it will seed
the randomizer using the system clock. If -shuffle is set to an
integer N, then N will be used as the seed value. In both cases,
the seed will be reported for reproducibility.
-skip regexp
Run only those tests, examples, fuzz tests, and benchmarks that
do not match the regular expression. Like for -run and -bench,
for tests and benchmarks, the regular expression is split by unbracketed
slash (/) characters into a sequence of regular expressions, and each
part of a test's identifier must match the corresponding element in
the sequence, if any.
-timeout d
If a test binary runs longer than duration d, panic.
If d is 0, the timeout is disabled.
The default is 10 minutes (10m).
-v
Verbose output: log all tests as they are run. Also print all
text from Log and Logf calls even if the test succeeds.
-vet list
Configure the invocation of "go vet" during "go test"
to use the comma-separated list of vet checks.
If list is empty, "go test" runs "go vet" with a curated list of
checks believed to be always worth addressing.
If list is "off", "go test" does not run "go vet" at all.
The following flags are also recognized by 'go test' and can be used to
profile the tests during execution:
-benchmem
Print memory allocation statistics for benchmarks.
Allocations made in C or using C.malloc are not counted.
-blockprofile block.out
Write a goroutine blocking profile to the specified file
when all tests are complete.
Writes test binary as -c would.
-blockprofilerate n
Control the detail provided in goroutine blocking profiles by
calling runtime.SetBlockProfileRate with n.
See 'go doc runtime.SetBlockProfileRate'.
The profiler aims to sample, on average, one blocking event every
n nanoseconds the program spends blocked. By default,
if -test.blockprofile is set without this flag, all blocking events
are recorded, equivalent to -test.blockprofilerate=1.
-coverprofile cover.out
Write a coverage profile to the file after all tests have passed.
Sets -cover.
-cpuprofile cpu.out
Write a CPU profile to the specified file before exiting.
Writes test binary as -c would.
-memprofile mem.out
Write an allocation profile to the file after all tests have passed.
Writes test binary as -c would.
-memprofilerate n
Enable more precise (and expensive) memory allocation profiles by
setting runtime.MemProfileRate. See 'go doc runtime.MemProfileRate'.
To profile all memory allocations, use -test.memprofilerate=1.
-mutexprofile mutex.out
Write a mutex contention profile to the specified file
when all tests are complete.
Writes test binary as -c would.
-mutexprofilefraction n
Sample 1 in n stack traces of goroutines holding a
contended mutex.
-outputdir directory
Place output files from profiling in the specified directory,
by default the directory in which "go test" is running.
-trace trace.out
Write an execution trace to the specified file before exiting.
Each of these flags is also recognized with an optional 'test.' prefix,
as in -test.v. When invoking the generated test binary (the result of
'go test -c') directly, however, the prefix is mandatory.
The 'go test' command rewrites or removes recognized flags,
as appropriate, both before and after the optional package list,
before invoking the test binary.
For instance, the command
go test -v -myflag testdata -cpuprofile=prof.out -x
will compile the test binary and then run it as
pkg.test -test.v -myflag testdata -test.cpuprofile=prof.out
(The -x flag is removed because it applies only to the go command's
execution, not to the test itself.)
The test flags that generate profiles (other than for coverage) also
leave the test binary in pkg.test for use when analyzing the profiles.
When 'go test' runs a test binary, it does so from within the
corresponding package's source code directory. Depending on the test,
it may be necessary to do the same when invoking a generated test
binary directly. Because that directory may be located within the
module cache, which may be read-only and is verified by checksums, the
test must not write to it or any other directory within the module
unless explicitly requested by the user (such as with the -fuzz flag,
which writes failures to testdata/fuzz).
The command-line package list, if present, must appear before any
flag not known to the go test command. Continuing the example above,
the package list would have to appear before -myflag, but could appear
on either side of -v.
When 'go test' runs in package list mode, 'go test' caches successful
package test results to avoid unnecessary repeated running of tests. To
disable test caching, use any test flag or argument other than the
cacheable flags. The idiomatic way to disable test caching explicitly
is to use -count=1.
To keep an argument for a test binary from being interpreted as a
known flag or a package name, use -args (see 'go help test') which
passes the remainder of the command line through to the test binary
uninterpreted and unaltered.
For instance, the command
go test -v -args -x -v
will compile the test binary and then run it as
pkg.test -test.v -x -v
Similarly,
go test -args math
will compile the test binary and then run it as
pkg.test math
In the first example, the -x and the second -v are passed through to the
test binary unchanged and with no effect on the go command itself.
In the second example, the argument math is passed through to the test
binary, instead of being interpreted as the package list.
フラグの一覧をまとめてみました!
-bench
-benchtime
-count
-cover
-covermode
-coverpkg
-cpu
-failfast
-fullpath
-fuzz
-fuzztime
-fuzzminimizetime
-json
-list
-parallel
-run
-short
-shuffle
-skip
-timeout
-v
-vet
-benchmem
-blockprofile
-blockprofilerate
-coverprofile
-cpuprofile
-memprofile
-memprofilerate
-mutexprofile
-mutexprofilefraction
-outputdir
-trace
全部で33個もあります!
フラグは以下の2種類に分類できるようです。
- テストの実行を制御するフラグ(1〜22まで)
- テストの実行中にテスト自体をプロファイルするフラグ(23〜33まで)
実際にそれぞれのフラグを見ていきましょう!
まずはテストの実行についての記述を見てみる
フラグの解説の前にテストの実行の詳細を見てみます(興味のない方はスキップしてもらっても構いません)。
具体的には「Each of these flags is also recognized with ...」の部分の内容を詳しくみていきたいと思います。
サマリとしては以下のようになります。
- テストのバイナリを
go test -c
で生成することができる - テストのバイナリによるテスト実行をする場合、各フラグは
test.
のprefixをつける必要がある(たとえば-v
は-test.v
として指定する必要がある) - プロファイルを生成するテストフラグもテストのバイナリを残す(残されたバイナリはプロファイルを分析するときに使用されます)
-
go test
は成功したテストをキャッシュし、余計なテストが実行されないようになっている
go help test
をみてみると、go test
コマンド自体が受け付けるフラグの詳細をみることができます。
サンプルコードについての補足
今回、テストの挙動がよりわかりやすくなるようにフラグを付与してテストを実行しています。
とくに記載のない限りは、以下のコードでテスト行っています。
main.go
package main
import "slices"
func Sum(nums []int) int {
var sum int
for v := range slices.Values(nums) {
sum += v
}
return sum
}
-
main_test.go
(ベンチマーク)
package main
import "testing"
func BenchmarkTest(b *testing.B) {
nums := []int{1, 2, 3, 4, 5}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = Sum(nums)
}
}
-
main_test.go
(ベンチマーク以外)
package main
import "testing"
func TestSum(t *testing.T) {
nums := []int{1, 2, 3, 4, 5}
expected := 15
if acutal := Sum(nums); acutal != expected {
t.Errorf("Expected %d but got %d", expected, acutal)
}
}
テストの実行を制御するフラグ
-bench
フラグ
1. シグネチャと詳細は以下になります。
-bench regexp
Run only those benchmarks matching a regular expression.
By default, no benchmarks are run.
To run all benchmarks, use '-bench .' or '-bench=.'.
The regular expression is split by unbracketed slash (/)
characters into a sequence of regular expressions, and each
part of a benchmark's identifier must match the corresponding
element in the sequence, if any. Possible parents of matches
are run with b.N=1 to identify sub-benchmarks. For example,
given -bench=X/Y, top-level benchmarks matching X are run
with b.N=1 to find any sub-benchmarks matching Y, which are
then run in full.
-bench
フラグはベンチマークのテストを実行するためのフラグです。デフォルトではベンチマークのテストは実行されず、-bench .
か -bench=.
のフラグを付与するとベンチマークのテストが実行されます。たとえば、-bench=X/Yを指定すると、Xにマッチするトップレベルのベンチマークがb.N=1で実行され、Yにマッチするサブベンチマークが見つかり、それがフルで実行されます。
実際に確かめてみましょう。
冒頭に記述したベンチマークの main_test.go
を使用します。
❯ go test .
ok github.com/k3forx/testflag 0.504s [no tests to run]
.
のみだと「no tests to run」という表示になります。
-bench
を指定して実行してみると、ちゃんとベンチマークが実行されました!
❯ go test -bench .
goos: darwin
goarch: arm64
pkg: github.com/k3forx/testflag
cpu: Apple M3 Pro
BenchmarkTest-11 310160636 3.872 ns/op
PASS
ok github.com/k3forx/testflag 1.836s
サブテストについては https://go.dev/blog/subtests が参考になります。
-benchtime
フラグ
2. シグネチャと詳細は以下になります。
-benchtime t
Run enough iterations of each benchmark to take t, specified
as a time.Duration (for example, -benchtime 1h30s).
The default is 1 second (1s).
The special syntax Nx means to run the benchmark N times
(for example, -benchtime 100x).
-benchtime
はベンチマークのテストを実行する時間を指定するようです。ただし、このフラグには特別なシンタックスがあり Nx
と記述された際には、N
回だけベンチマークテストを実行するようになっているようです。
実際に見てみましょう。
ベンチマークを実行してみます。
❯ go test -benchtime 1s -bench .
goos: darwin
goarch: arm64
pkg: github.com/k3forx/testflag
cpu: Apple M3 Pro
BenchmarkSum-11 314220164 3.876 ns/op
PASS
ok github.com/k3forx/testflag 1.750s
❯ go test -benchtime 0.5s -bench .
goos: darwin
goarch: arm64
pkg: github.com/k3forx/testflag
cpu: Apple M3 Pro
BenchmarkSum-11 153570301 3.858 ns/op
PASS
ok github.com/k3forx/testflag 1.136s
0.5s
を指定した際には反復回数が 153570301
になっており、1s
の 314220164
のおよそ半分になっています。実行時間が半分になったので、反復回数も半分になったのだと考えられますね!
Nx
の方はどうでしょうか?
❯ go test -benchtime 100x -bench .
goos: darwin
goarch: arm64
pkg: github.com/k3forx/testflag
cpu: Apple M3 Pro
BenchmarkSum-11 100 12.50 ns/op
PASS
ok github.com/k3forx/testflag 0.163s
確かに100回しかテストが実行されていないようです!
-count
フラグ
3. シグネチャと詳細は以下になります。
-count n
Run each test, benchmark, and fuzz seed n times (default 1).
If -cpu is set, run n times for each GOMAXPROCS value.
Examples are always run once. -count does not apply to
fuzz tests matched by -fuzz.
-count
フラグはテストを実行する回数を指定するフラグになります。-cpu
フラグがある場合、それぞれの GOMAXPROCS
に対して n
回テストを実行するようです (-cpu
フラグと GOMAXPROCS
については後述します!)。
実際に見てみましょう!
❯ go test . -count=1
ok github.com/k3forx/testflag 0.165s
❯ go test . -count=2
ok github.com/k3forx/testflag 0.166s
簡単なテストなので、1回分多く実行した差がほとんどみられないですね。まだ解説していないのですが、-v
のフラグをつけるとテストの実行ログを確認できるので、そのオプションをつけて再度実行してみます。
❯ go test . -count=1 -v
=== RUN TestSum
--- PASS: TestSum (0.00s)
PASS
ok github.com/k3forx/testflag 0.197s
❯ go test . -count=2 -v
=== RUN TestSum
--- PASS: TestSum (0.00s)
=== RUN TestSum
--- PASS: TestSum (0.00s)
PASS
ok github.com/k3forx/testflag 0.162s
確かに -count
で指定された回数分のテストが実行されていそうでした!
余談ですが、この -count=1
フラグはキャッシュを無効にするフラグとして使われることが多い印象です。go help testflag
の記述の中にも以下のような文章があります。
When 'go test' runs in package list mode, 'go test' caches successful
package test results to avoid unnecessary repeated running of tests. To
disable test caching, use any test flag or argument other than the
cacheable flags. The idiomatic way to disable test caching explicitly
is to use -count=1.
-cover
フラグ
4. シグネチャと詳細は以下になります。
-cover
Enable coverage analysis.
Note that because coverage works by annotating the source
code before compilation, compilation and test failures with
coverage enabled may report line numbers that don't correspond
to the original sources.
-cover
はカバレッジ分析を有効にするフラグです。注意事項として「カバレッジはコンパイル前にソースコードに注釈を付けることで機能するため、カバレッジを有効にしてコンパイルやテストに失敗すると、元のソースと一致しない行番号が報告される可能性がある」と補足があります。
実際に試してみます!
❯ go test . -cover
ok github.com/k3forx/testflag 0.203s coverage: 100.0% of statements
-covermode
5. シグネチャと詳細は以下になります。
-covermode set,count,atomic
Set the mode for coverage analysis for the package[s]
being tested. The default is "set" unless -race is enabled,
in which case it is "atomic".
The values:
set: bool: does this statement run?
count: int: how many times does this statement run?
atomic: int: count, but correct in multithreaded tests;
significantly more expensive.
Sets -cover.
-covermode
フラグはカバレッジ分析に用いられるモードを指定するフラグです。デフォルトは set
になっていますが、 -race
フラグが指定されていると atomic
になるようです。「Sets -cover」と記述してあるように -cover
の機能も含むフラグとなっています!
指定可能な値は以下のようになっています。
-
set
: 実行されたかどうか(デフォルト) -
count
: 何回実行されたか -
atomic
: マルチスレッドでのテストで正確に何回実行されたか(実行コストは増える)
実際に指定してみるとわかるのですが、 -covermode
の値を変えてテストを実行してみてもほとんど差分は分かりません。-coverprofile
フラグと使用されることが多いので、あとで差分を詳しくみてみたいと思います!
-coverpkg
6. シグネチャと詳細は以下になります。
-coverpkg pattern1,pattern2,pattern3
Apply coverage analysis in each test to packages matching the patterns.
The default is for each test to analyze only the package being tested.
See 'go help packages' for a description of package patterns.
Sets -cover.
-coverpkg
フラグは各テストで、パターンにマッチするパッケージに対してカバレッジ解析するためのフラグです。デフォルトでは、各テストはテスト対象のパッケージのみを分析します。
また -cover
フラグをカバーしているので、-coverpkg
を指定している場合は -cover
フラグが不要になります。
と説明しましたが、実際のコードで説明したほうがわかりやすいと思うので、具体例を見ていきたいと思います。
今回は以下のようなディレクトリ構成でテストを実行することを考えてみます。それぞれのテストコードは同じパッケージのテストしか記述していないとします。実際のコードやテストコードはとくに大事でないので、ここでは記述していません。
❯ tree .
.
├── go.mod
├── main.go
├── main_test.go # main.goのテストのみが記述されている
└── pkg
├── a
│ ├── a.go
│ └── a_test.go # a.goのテストのみが記述されている
└── b
├── b.go
└── b_test.go # b.goのテストのみが記述されている
4 directories, 7 files
-coverpkg
フラグなしの場合と、-coverpkg=.
フラグ、-coverpkg=./...
フラグでどのように差分が出るかみてみます!
# `-coverpkg` フラグなし
❯ go test ./... -cover
ok github.com/k3forx/testflag 0.960s coverage: 100.0% of statements
ok github.com/k3forx/testflag/pkg/a 1.337s coverage: 100.0% of statements
ok github.com/k3forx/testflag/pkg/b 1.146s coverage: 100.0% of statements
# `-coverpkg=.` フラグ
❯ go test ./... -coverpkg=.
ok github.com/k3forx/testflag 0.188s coverage: 100.0% of statements in .
ok github.com/k3forx/testflag/pkg/a 0.360s coverage: 0.0% of statements in .
ok github.com/k3forx/testflag/pkg/b 0.546s coverage: 0.0% of statements in .
# `-coverpkg=./...` フラグ
❯ go test ./... -coverpkg=./...
ok github.com/k3forx/testflag 0.232s coverage: 33.3% of statements in ./...
ok github.com/k3forx/testflag/pkg/a 0.422s coverage: 33.3% of statements in ./...
ok github.com/k3forx/testflag/pkg/b 0.600s coverage: 33.3% of statements in ./...
-coverpkg
で指定されたパッケージに対して、実行されたテストがどれだけのカバレッジを持っているかを示しているようです。
たとえば -coverpkg=.
を指定した場合、テストを実行した階層と同じ階層にあるコードに対してどれだけのカバレッジがあるかが計測されます。pkg/a
と pkg/b
には main.go
のテストコードが記述されていないので、coverage: 0.0% of statements in .
と表示されます。
では、-coverpkg=./pkg/a
と指定した場合はどうなるでしょうか?
ここまでの説明をもとに考えると pkg/a
の部分には coverage: 100% of ...
と表示され、それ以外の部分は coverage: 0.0% of ...
と表示されることが予想できますね。
実際に試してみます!
❯ go test ./... -coverpkg=./pkg/a
ok github.com/k3forx/testflag 0.201s coverage: 0.0% of statements in ./pkg/a
ok github.com/k3forx/testflag/pkg/a 0.384s coverage: 100.0% of statements in ./pkg/a
ok github.com/k3forx/testflag/pkg/b 0.565s coverage: 0.0% of statements in ./pkg/a
予想通りの結果になりました!
余談ですが、 ./...
の記述については go help packages
を参照するとより詳細な説明が得られます。
-cpu
7. シグネチャと詳細は以下になります。
-cpu 1,2,4
Specify a list of GOMAXPROCS values for which the tests, benchmarks or
fuzz tests should be executed. The default is the current value
of GOMAXPROCS. -cpu does not apply to fuzz tests matched by -fuzz.
-cpu
フラグは 1,2,4
などの数字のリストを指定でき、実行されるべきテストやベンチマークに対する GOMAXPROCS
のリストを指定できます。デフォルトの値は GOMAXPROCS
の現在の値になります。
実際に試してみます!
異なるCPU数で実行して、ロックや同期が期待通りに機能するか確認するためのテストを書いてみます。
main_test.go
package main
import (
"sync"
"testing"
)
func parallelSum(values []int) int {
var (
sum int
mu sync.Mutex
wg sync.WaitGroup
)
for _, v := range values {
wg.Add(1)
go func(val int) {
defer wg.Done()
mu.Lock()
sum += val
mu.Unlock()
}(v)
}
wg.Wait()
return sum
}
func TestParallelSum(t *testing.T) {
values := []int{1, 2, 3, 4, 5}
result := parallelSum(values)
expected := 15
if result != expected {
t.Errorf("Expected %d, got %d", expected, result)
}
}
❯ go test . -cpu 1,12 -v
=== RUN TestParallelSum
--- PASS: TestParallelSum (0.00s)
=== RUN TestParallelSum
--- PASS: TestParallelSum (0.00s)
PASS
ok github.com/k3forx/testflag 0.164s
ちゃんと動作していることが確かめられました!
並列性を考慮したテストが各CPU設定で正常に動作するかを確認するために使えそうです。
-failfast
8. シグネチャと詳細は以下になります。
-failfast
Do not start new tests after the first test failure.
-failfast
フラグは最初のテストが失敗したら、次のテストを実行させないようにするためのフラグです。
実際に見てみましょう!
package main
import (
"testing"
)
func TestSum(t *testing.T) {
cases := map[string]struct {
nums []int
expected int
}{
"ok_not_zero": {
nums: []int{1, 2, 3, 4, 5},
expected: 15,
},
"ng_not_zero": {
nums: []int{1, 2, 3, 4, 5},
expected: 0,
},
"ok_zero": {
nums: []int{},
expected: 0,
},
}
for name, c := range cases {
t.Run(name, func(t *testing.T) {
if acutal := Sum(c.nums); acutal != c.expected {
t.Errorf("Expected %d but got %d", c.expected, acutal)
}
})
}
}
とくに -failfast
フラグを指定さずに実行してみます。
❯ go test . -v
=== RUN TestSum
=== RUN TestSum/ok_not_zero
=== RUN TestSum/ng_not_zero
main_test.go:29: Expected 0 but got 15
=== RUN TestSum/ok_zero
--- FAIL: TestSum (0.00s)
--- PASS: TestSum/ok_not_zero (0.00s)
--- FAIL: TestSum/ng_not_zero (0.00s)
--- PASS: TestSum/ok_zero (0.00s)
FAIL
FAIL github.com/k3forx/testflag 0.161s
FAIL
すべてのテストケースが実行されていますね!
次は -failfast
フラグを指定してみます。
❯ go test . -v -failfast
=== RUN TestSum
=== RUN TestSum/ok_not_zero
=== RUN TestSum/ng_not_zero
main_test.go:29: Expected 0 but got 15
--- FAIL: TestSum (0.00s)
--- PASS: TestSum/ok_not_zero (0.00s)
--- FAIL: TestSum/ng_not_zero (0.00s)
FAIL
FAIL github.com/k3forx/testflag 0.165s
FAIL
先ほどまで実行されていた ok_zero
のテストケースが実行されてません。-failfast
フラグを指定すると失敗したテスト以降のテストは実行されないことが確かめられました!
-fullpath
9. シグネチャと詳細は以下になります。
-fullpath
Show full file names in the error messages.
-fullpath
フラグを指定すればエラーメッセージの中のファイル名をフルパスで表示できます。
実際に確かめてみます!
フラグをつけて実行すると、以下のように失敗したテストの部分がフルパスで表示されていました!
❯ go test . -fullpath
--- FAIL: TestSum (0.00s)
/Users/XXX/repos/scrap/testflag/main_test.go:12: Expected 0 but got 15
FAIL
FAIL github.com/k3forx/testflag 0.163s
FAIL
-fuzz
10. シグネチャと詳細は以下になります。
-fuzz regexp
Run the fuzz test matching the regular expression. When specified,
the command line argument must match exactly one package within the
main module, and regexp must match exactly one fuzz test within
that package. Fuzzing will occur after tests, benchmarks, seed corpora
of other fuzz tests, and examples have completed. See the Fuzzing
section of the testing package documentation for details.
-fuzz
フラグは正規表現にマッチしたfuzzテストを実行するためのフラグです。このフラグが指定されたとき、コマンドラインの引数はmainモジュール内の1つのパッケージにマッチしなければならず、かつ、正規表現はそのパッケージ内にある1つのfuzzテストとマッチしなければなりません。また、fuzzテストはテスト、ベンチマーク、seed corpora、他のfuzzテストとexampleテストが終了したのちに実行されます。
fuzzテストに関しては以下の公式ドキュメントやチュートリアルを読んでいただくと良いと思います。
実際に試してみます!
文字列を逆転させる Reverse
関数を実装してみました。日本語でも問題ないように []rune
を使っているので、実装自体はできているように見えます!
main.go
package main
import "slices"
func Reverse(str string) string {
r := []rune(str)
slices.Reverse(r)
return string(r)
}
main_test.go
package main
import "testing"
func TestReverse(t *testing.T) {
cases := map[string]struct {
str string
}{
"empty": {
str: "",
},
"success_abc": {
str: "abc",
},
"success_123": {
str: "123",
},
"success_あいう": {
str: "あいう",
},
}
for name, c := range cases {
t.Run(name, func(t *testing.T) {
original := Reverse(Reverse(c.str))
if original != c.str {
t.Errorf("'%s' should be '%s'", original, c.str)
}
})
}
}
まずは通常のテストを実行して、実装が問題ないかみてみます。
❯ go test .
ok github.com/k3forx/fuzztest 0.324s
問題なさそうですね!
続いて、以下のようなfuzzテストを main_test.go
に追加して、fuzzテストを実行してみます。
func FuzzReverse(f *testing.F) {
for _, seed := range []string{"", "abc", "123"} {
f.Add(seed)
}
f.Fuzz(func(t *testing.T, str string) {
original := Reverse(Reverse(str))
if original != str {
t.Errorf("'%s' should be '%s'", original, str)
}
})
}
実行結果は以下のようになりました。今の実装に考慮漏れがありそうですね!
また、フラグの説明通り、通常のテストが実行されたのちにfuzzテストが実行されていることも確かめられました!
❯ go test -fuzz . -v
=== RUN TestReverse
=== RUN TestReverse/empty
=== RUN TestReverse/success_abc
=== RUN TestReverse/success_123
=== RUN TestReverse/success_あいう
--- PASS: TestReverse (0.00s)
--- PASS: TestReverse/empty (0.00s)
--- PASS: TestReverse/success_abc (0.00s)
--- PASS: TestReverse/success_123 (0.00s)
--- PASS: TestReverse/success_あいう (0.00s)
=== RUN FuzzReverse
fuzz: elapsed: 0s, gathering baseline coverage: 0/6 completed
failure while testing seed corpus entry: FuzzReverse/15bd0ea6a71e1138
fuzz: elapsed: 0s, gathering baseline coverage: 3/6 completed
--- FAIL: FuzzReverse (0.04s)
--- FAIL: FuzzReverse (0.00s)
main_test.go:12: '�' should be '�'
=== NAME
FAIL
exit status 1
FAIL github.com/k3forx/fuzztest 0.419s
ちなみにfuzzテストに失敗した場合は、その時のログが testdata/fuzz/${テスト名}
ディレクトリ配下に追加されます。
-fuzztime
11. シグネチャと詳細は以下になります。
-fuzztime t
Run enough iterations of the fuzz target during fuzzing to take t,
specified as a time.Duration (for example, -fuzztime 1h30s).
The default is to run forever.
The special syntax Nx means to run the fuzz target N times
(for example, -fuzztime 1000x).
-fuzztime
フラグはfuzzテストを実行する時間を指定するためのフラグです。time.Duration
の形式で指定します。デフォルトだと無限に実行されます。Nx
という特別なsyntaxが用意されており、fuzzテストを N
回実行するフラグになっています。
実際に試してみます!
fuzzテストが無限に続くような簡単な関数を実装してみました。
main.go
package main
func DoNothing(str string) string {
return str
}
main_test.go
package main
import "testing"
func FuzzDoNothing(f *testing.F) {
for _, seed := range []string{"", "abc", "123"} {
f.Add(seed)
}
f.Fuzz(func(t *testing.T, str string) {
original := DoNothing(str)
if original != str {
t.Errorf("'%s' should be '%s'", original, str)
}
})
}
-fuzztime
フラグを指定しないと永遠にfuzzテストが実行されます(以下のログはCtrl + Cで中断したときのログになります)。
❯ go test -fuzz .
fuzz: elapsed: 0s, gathering baseline coverage: 0/3 completed
fuzz: elapsed: 0s, gathering baseline coverage: 3/3 completed, now fuzzing with 16 workers
fuzz: elapsed: 3s, execs: 740969 (246985/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 6s, execs: 1592199 (283653/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 9s, execs: 2467434 (291831/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 12s, execs: 3239115 (257238/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 15s, execs: 3992998 (251284/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 18s, execs: 4801640 (269543/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 21s, execs: 5587572 (261988/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 24s, execs: 6394724 (269043/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 27s, execs: 7180623 (261970/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 30s, execs: 7967698 (262315/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 33s, execs: 8736557 (256317/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 36s, execs: 9526655 (263375/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 39s, execs: 10309036 (260783/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 42s, execs: 11076178 (255721/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 45s, execs: 11855857 (259882/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 48s, execs: 12626839 (257008/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 51s, execs: 13369611 (247573/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 54s, execs: 14134283 (254833/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 57s, execs: 14905019 (256990/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 1m0s, execs: 15660974 (251980/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 1m3s, execs: 16397227 (245348/sec), new interesting: 0 (total: 3)
^Cfuzz: elapsed: 1m5s, execs: 16802788 (233700/sec), new interesting: 0 (total: 3)
PASS
ok github.com/k3forx/fuzztest 65.084s
次は -fuzztime
フラグを指定して実行してみます。
❯ go test -fuzz . -fuzztime 3s
fuzz: elapsed: 0s, gathering baseline coverage: 0/3 completed
fuzz: elapsed: 0s, gathering baseline coverage: 3/3 completed, now fuzzing with 16 workers
fuzz: elapsed: 3s, execs: 868136 (289372/sec), new interesting: 0 (total: 3)
fuzz: elapsed: 3s, execs: 868136 (0/sec), new interesting: 0 (total: 3)
PASS
ok github.com/k3forx/fuzztest 3.461s
確かに3秒でfuzzテストが終了していますね!
-fuzzminimizetime
12. シグネチャと詳細は以下になります。
-fuzzminimizetime t
Run enough iterations of the fuzz target during each minimization
attempt to take t, as specified as a time.Duration (for example,
-fuzzminimizetime 30s).
The default is 60s.
The special syntax Nx means to run the fuzz target N times
(for example, -fuzzminimizetime 100x).
-fuzzminimizetime
フラグは各最小化試行中にfuzzターゲットのイテレーションを time.Duration
として指定される t
の時間だけ実行するためのフラグです。特別なsyntaxがあり、Nx
と指定されている場合は N
回実行するようになります。
「最小化」というのはFuzzテストの一般的な考えのことのようです。
テストに失敗するデータを見つけた際にそのデータ量を限りなく小さくすることを「最小化」と言っているようです。この最小化によってデバッグを容易にする目的のようです。詳細はGo Fuzzingを参照してください。
つまり、-fuzzminimizetime
フラグはその最小化を行うための時間を指定するためのフラグだと言えます。
実際に試してみます!
main.go
package main
import "slices"
func Reverse(str string) string {
r := []rune(str)
slices.Reverse(r)
return string(r)
}
main_test.go
package main
import (
"testing"
)
func FuzzReverse(f *testing.F) {
for _, seed := range []string{"", "abc", "123"} {
f.Add(seed)
}
f.Fuzz(func(t *testing.T, str string) {
original := Reverse(Reverse(str))
if original != str {
t.Errorf("'%s' should be '%s'", original, str)
}
})
}
-fuzzminimizetime
フラグとして 0
を指定してみて、失敗したケースを見てみます。
❯ go test -fuzz . -fuzzminimizetime=0s
fuzz: elapsed: 0s, gathering baseline coverage: 0/4 completed
fuzz: elapsed: 0s, gathering baseline coverage: 4/4 completed, now fuzzing with 11 workers
fuzz: elapsed: 0s, execs: 6 (263/sec), new interesting: 0 (total: 4)
--- FAIL: FuzzReverse (0.03s)
--- FAIL: FuzzReverse (0.00s)
main_test.go:14: '
b�' should be '
b�'
Failing input written to testdata/fuzz/FuzzReverse/e5eba58a36d29375
To re-run:
go test -run=FuzzReverse/e5eba58a36d29375
FAIL
exit status 1
FAIL github.com/k3forx/testflag 0.200s
❯ cat testdata/fuzz/FuzzReverse/e5eba58a36d29375
go test fuzz v1
string("\fb\x90")
\fb\x90
が失敗した入力として記録されています。
続いて、-fuzzminimizetime
フラグとして 100s
を指定し、失敗した入力がどう変化するかみてみます。
# 再度Fuzzテストを行おうとすると失敗したデータから始まってしまうので一旦削除
❯ rm testdata/fuzz/FuzzReverse/e5eba58a36d29375
❯ go test -fuzz . -fuzzminimizetime=100s
fuzz: elapsed: 0s, gathering baseline coverage: 0/4 completed
fuzz: elapsed: 0s, gathering baseline coverage: 4/4 completed, now fuzzing with 11 workers
fuzz: minimizing 45-byte failing input file
fuzz: elapsed: 0s, minimizing
--- FAIL: FuzzReverse (0.02s)
--- FAIL: FuzzReverse (0.00s)
main_test.go:14: '�' should be '�'
Failing input written to testdata/fuzz/FuzzReverse/e504fa449c34beb2
To re-run:
go test -run=FuzzReverse/e504fa449c34beb2
FAIL
exit status 1
FAIL github.com/k3forx/testflag 0.192s
❯ cat testdata/fuzz/FuzzReverse/e504fa449c34beb2
go test fuzz v1
string("\xc6")
\xc6
が失敗データとして記録されており、確かに先ほどの \fb\x90
よりデータ量として小さくなっていることが確かめられました!
-json
13. シグネチャと詳細は以下になります。
-json
Log verbose output and test results in JSON. This presents the
same information as the -v flag in a machine-readable format.
-json
フラグを指定すると、テストの詳細なログと結果をJSON形式で表示できます。これは、テストのログを見やすく表示する -v
フラグと同じ情報になります。
実際に見てみましょう!
package main
import (
"testing"
)
func TestSum(t *testing.T) {
nums := []int{1, 2, 3, 4, 5}
expected := 15
if acutal := Sum(nums); acutal != expected {
t.Errorf("Expected %d but got %d", expected, acutal)
}
}
-json
フラグを付与してテストを実行してみます。
❯ go test . -json | jq
{
"Time": "2024-10-22T21:51:09.327997+09:00",
"Action": "start",
"Package": "github.com/k3forx/testflag"
}
{
"Time": "2024-10-22T21:51:09.328047+09:00",
"Action": "run",
"Package": "github.com/k3forx/testflag",
"Test": "TestSum"
}
{
"Time": "2024-10-22T21:51:09.328049+09:00",
"Action": "output",
"Package": "github.com/k3forx/testflag",
"Test": "TestSum",
"Output": "=== RUN TestSum\n"
}
{
"Time": "2024-10-22T21:51:09.328054+09:00",
"Action": "output",
"Package": "github.com/k3forx/testflag",
"Test": "TestSum",
"Output": "--- PASS: TestSum (0.00s)\n"
}
{
"Time": "2024-10-22T21:51:09.328055+09:00",
"Action": "pass",
"Package": "github.com/k3forx/testflag",
"Test": "TestSum",
"Elapsed": 0
}
{
"Time": "2024-10-22T21:51:09.328058+09:00",
"Action": "output",
"Package": "github.com/k3forx/testflag",
"Output": "PASS\n"
}
{
"Time": "2024-10-22T21:51:09.328059+09:00",
"Action": "output",
"Package": "github.com/k3forx/testflag",
"Output": "ok \tgithub.com/k3forx/testflag\t(cached)\n"
}
{
"Time": "2024-10-22T21:51:09.328063+09:00",
"Action": "pass",
"Package": "github.com/k3forx/testflag",
"Elapsed": 0
}
確かにJSON形式でテストのログと結果が表示されていました!
-list
14. シグネチャと詳細は以下になります。
-list regexp
List tests, benchmarks, fuzz tests, or examples matching the regular
expression. No tests, benchmarks, fuzz tests, or examples will be run.
This will only list top-level tests. No subtest or subbenchmarks will be
shown.
-list
フラグを指定すると、指定された正規表現にマッチしたテストやベンチマーク、fuzzテスト、exampleテストを表示できます。どのテストも実行されません。このフラグはトップレベルのテストのみに適用され、サブテストやサブベンチマークテストは表示されないようになっています。
実際に確かめてみます!
TestSum
というテスト関数を用意して挙動を確かめてみます。正規表現なので Sum
, ^TestSum
を指定すると TestSum
が表示され、^Su
や Test$
だと表示されないことが予想されます。
❯ go test . -list Sum
TestSum
ok github.com/k3forx/testflag 0.246s
❯ go test . -list ^TestSum
TestSum
ok github.com/k3forx/testflag 0.159s
❯ go test . -list ^Su
ok github.com/k3forx/testflag 0.162s
❯ go test . -list Test$
ok github.com/k3forx/testflag 0.163s
確かに正規表現にマッチしたテスト名を取得できました!
-parallel
15. シグネチャと詳細は以下になります。
-parallel n
Allow parallel execution of test functions that call t.Parallel, and
fuzz targets that call t.Parallel when running the seed corpus.
The value of this flag is the maximum number of tests to run
simultaneously.
While fuzzing, the value of this flag is the maximum number of
subprocesses that may call the fuzz function simultaneously, regardless of
whether T.Parallel is called.
By default, -parallel is set to the value of GOMAXPROCS.
Setting -parallel to values higher than GOMAXPROCS may cause degraded
performance due to CPU contention, especially when fuzzing.
Note that -parallel only applies within a single test binary.
The 'go test' command may run tests for different packages
in parallel as well, according to the setting of the -p flag
(see 'go help build').
-parallel
フラグは t.Parallel
を呼んでいるテスト関数の並列実行を許可するためのフラグです。このフラグの値は同時に実行するテストの最大数を指定するものです。注意点として、GOMAXPROCS
より大きい値を指定するとパフォーマンスに影響が出ることと、このフラグは1つのテストバイナリの中でしか有効にならないことの2つが挙げられています。
ここで気になるのは
- テストのバイナリはどの単位で生成されるのか?
- テストのバイナリ同士を並列に実行するためにはどうすれば良いのか?
ということだと思います。
2つ目の質問に対する回答は go help testflag
の中にヒントが隠されており、-p
フラグが並列性を制御するためのフラグになります。実際に go help build
を実行すると、以下のような説明が得られます。
-p n
the number of programs, such as build commands or
test binaries, that can be run in parallel.
The default is GOMAXPROCS, normally the number of CPUs available.
「the number of programs, such as build commands or test binaries, that can be run in parallel.」とあるように、テストのバイナリを並列に実行するためには -p
フラグを使用すれば良いことがわかります。
最初の問いについて考えてみます。
わかりやすいように以下のようなディレクトリ構成で、テストバイナリを生成してみます。
❯ tree .
.
├── go.mod
├── main.go
├── main_test.go // package main
└── pkg
├── a
│ └── a_test.go // package a_test
└── b
└── b_test.go // package b_test
4 directories, 5 files
テストバイナリは -c
フラグで生成できます。
❯ go test -c ./...
❯ ls
a.test b.test go.mod main.go main_test.go pkg testflag.test
a.test
, b.test
, testflag.test
の3つのテストバイナリが生成されており、package単位で生成されていそうということがわかると思います。
ある程度挙動がわかってきたので実際に試してみます!
先ほどのディレクトリ構成のテストを以下のように実装してみます。
とく何をしているわけでもなく、どのテストケースでも time.Sleep(3 * time.Second)
を呼び出すようにしているだけです。またサブテストは t.Parallel
を許可しています。
main_test.go
package b_test
import (
"testing"
"time"
)
func TestB(t *testing.T) {
cases := map[string]struct {
num int
}{
"case1": {num: 1},
"case2": {num: 2},
"case3": {num: 3},
}
for name := range cases {
t.Run(name, func(t *testing.T) {
t.Parallel()
time.Sleep(3 * time.Second)
t.Logf("%s done at %v", t.Name(), time.Now())
})
}
}
a_test.go
package a_test
import (
"testing"
"time"
)
func TestA(t *testing.T) {
cases := map[string]struct {
num int
}{
"case1": {num: 1},
"case2": {num: 2},
"case3": {num: 3},
}
for name := range cases {
t.Run(name, func(t *testing.T) {
t.Parallel()
time.Sleep(3 * time.Second)
t.Logf("%s done at %v", t.Name(), time.Now())
})
}
}
b_test.go
package b_test
import (
"testing"
"time"
)
func TestB(t *testing.T) {
cases := map[string]struct {
num int
}{
"case1": {num: 1},
"case2": {num: 2},
"case3": {num: 3},
}
for name := range cases {
t.Run(name, func(t *testing.T) {
t.Parallel()
time.Sleep(3 * time.Second)
t.Logf("%s done at %v", t.Name(), time.Now())
})
}
}
-parallel
フラグも -p
フラグもどちらもデフォルトの数が GOMAXPROCS
になっています。わかりやすいようにまずは 「パッケージごとに実行し、同一パッケージ内は並列処理を行う」 という条件のもとでテストを実行してみます。
-p
フラグがパッケージ単位の並列を指定し、-parallel
フラグが同一パッケージ内の並列度を指定するので、-p 1 -parallel 2
としてみます。
❯ go test ./... -v -parallel 2 -p 1 | grep _test.go
main_test.go:22: TestMain/case3 done at 2024-10-30 22:10:15.949223 +0900 JST m=+3.001105918
main_test.go:22: TestMain/case2 done at 2024-10-30 22:10:15.949207 +0900 JST m=+3.001090168
main_test.go:22: TestMain/case1 done at 2024-10-30 22:10:18.950911 +0900 JST m=+6.002810251
a_test.go:22: TestA/case2 done at 2024-10-30 22:10:22.21671 +0900 JST m=+3.001957043
a_test.go:22: TestA/case1 done at 2024-10-30 22:10:22.216719 +0900 JST m=+3.001966335
a_test.go:22: TestA/case3 done at 2024-10-30 22:10:25.218075 +0900 JST m=+6.003337376
b_test.go:22: TestB/case1 done at 2024-10-30 22:10:28.481415 +0900 JST m=+3.001872084
b_test.go:22: TestB/case3 done at 2024-10-30 22:10:28.481405 +0900 JST m=+3.001861793
b_test.go:22: TestB/case2 done at 2024-10-30 22:10:31.48306 +0900 JST m=+6.003532501
確かに意図した挙動になっていそうですね!
どのパッケージも同時刻に終了したサブテストが2つあり、最後の1つのサブテストが終わってから、次のパッケージのテストが実行されています!
次に -p 2 -parallel 2
として実行してみます!
❯ go test ./... -count=1 -v -parallel 2 -p 2 | grep _test.go
main_test.go:22: TestMain/case3 done at 2024-10-30 22:13:39.015679 +0900 JST m=+3.001437751
main_test.go:22: TestMain/case1 done at 2024-10-30 22:13:39.015699 +0900 JST m=+3.001457709
main_test.go:22: TestMain/case2 done at 2024-10-30 22:13:42.017176 +0900 JST m=+6.002949793
a_test.go:22: TestA/case1 done at 2024-10-30 22:13:39.134682 +0900 JST m=+3.002205460
a_test.go:22: TestA/case2 done at 2024-10-30 22:13:39.134649 +0900 JST m=+3.002172126
a_test.go:22: TestA/case3 done at 2024-10-30 22:13:42.137257 +0900 JST m=+6.004795543
b_test.go:22: TestB/case1 done at 2024-10-30 22:13:45.292284 +0900 JST m=+3.001975751
b_test.go:22: TestB/case3 done at 2024-10-30 22:13:45.292252 +0900 JST m=+3.001943626
b_test.go:22: TestB/case2 done at 2024-10-30 22:13:48.294393 +0900 JST m=+6.004099792
少しわかりにくいと思うので、時系列としてまとめてみます。
-
TestMain/case3
,TestMain/case1
,TestA/case1
,TestA/case2
が2024-10-30 22:13:39に終了- 並列できるパッケージは2つで、かつ、そのパッケージ内の並列度も2なので期待通りですね
-
TestMain/case2
,TestA/case3
が2024-10-30 22:13:42に終了- 並列できるパッケージは2つのままで、残ったテストが実行されています
-
TestB/case1
,TestB/case3
が2024-10-30 22:13:45に終了- 並列できるパッケージは2つだが、
TestMain
とTestA
のテストはすべて終了しているので、TestB
のサブテストが並列実行される
- 並列できるパッケージは2つだが、
-
TestB/case2
が2024-10-30 22:13:48に終了- 残ったテストが終了する
確かに意図した挙動になっていそうでした!
-run
16. シグネチャと詳細は以下になります。
-run regexp
Run only those tests, examples, and fuzz tests matching the regular
expression. For tests, the regular expression is split by unbracketed
slash (/) characters into a sequence of regular expressions, and each
part of a test's identifier must match the corresponding element in
the sequence, if any. Note that possible parents of matches are
run too, so that -run=X/Y matches and runs and reports the result
of all tests matching X, even those without sub-tests matching Y,
because it must run them to look for those sub-tests.
See also -skip.
-run
フラグは、指定された正規表現にマッチしたテストを実行します。テストに関して、正規表現は /
によって一連の正規表現に分割され、テストの識別子はその一連の正規表現の要素に一致しなければなりません。-run=X/Y
が指定された場合、Y
に合致するサブテストがなかったとしても、X
にマッチするテストはすべて実行されます。
実際に試してみます!
main.go
package main
func SumInt(a []int) int {
sum := 0
for _, v := range a {
sum += v
}
return sum
}
func SumInt64(a []int64) int64 {
sum := int64(0)
for _, v := range a {
sum += v
}
return sum
}
main_test.go
package main
import (
"testing"
)
func TestSumInt(t *testing.T) {
cases := map[string]struct {
input []int
expected int
}{
"empty": {
input: []int{},
expected: 0,
},
"result_15": {
input: []int{1, 2, 3, 4, 5},
expected: 15,
},
"result_115": {
input: []int{10, 20, 30, 40, 15},
expected: 115,
},
}
for name, c := range cases {
t.Run(name, func(t *testing.T) {
actual := SumInt(c.input)
if actual != c.expected {
t.Errorf("expected %d, got %d", c.expected, actual)
}
})
}
}
func TestSumInt64(t *testing.T) {
cases := map[string]struct {
input []int64
expected int64
}{
"empty": {
input: []int64{},
expected: 0,
},
"result_15": {
input: []int64{1, 2, 3, 4, 5},
expected: 15,
},
"result_115": {
input: []int64{10, 20, 30, 40, 15},
expected: 115,
},
}
for name, c := range cases {
t.Run(name, func(t *testing.T) {
actual := SumInt64(c.input)
if actual != c.expected {
t.Errorf("expected %d, got %d", c.expected, actual)
}
})
}
}
TestSumInt
のみを実行してみたいと思います
❯ go test . -v -run "TestSumInt$"
=== RUN TestSumInt
=== RUN TestSumInt/result_115
=== RUN TestSumInt/empty
=== RUN TestSumInt/result_15
--- PASS: TestSumInt (0.00s)
--- PASS: TestSumInt/result_115 (0.00s)
--- PASS: TestSumInt/empty (0.00s)
--- PASS: TestSumInt/result_15 (0.00s)
PASS
ok github.com/k3forx/testflag 0.164s
ちゃんと TestSumInt
のみが実行されてますね!
ちなみに -run "SumInt"
として指定すると、TestSumInt
と TestSumInt64
のテストが実行されます。
サブテストの方も見てみます!
/
で分けて正規表現が適用されるので、TestSumInt
のテストの result_15
のサブテストのみを実行したい場合、-run "Int$/result_15$"
と指定すれば良さそうですね。
❯ go test . -v -run "Int$/result_15$"
=== RUN TestSumInt
=== RUN TestSumInt/result_15
--- PASS: TestSumInt (0.00s)
--- PASS: TestSumInt/result_15 (0.00s)
PASS
ok github.com/k3forx/testflag 0.162s
確かに指定通りのテストのみが実行されていそうでした!
-short
17. シグネチャと詳細は以下になります。
-short
Tell long-running tests to shorten their run time.
It is off by default but set during all.bash so that installing
the Go tree can run a sanity check but not spend time running
exhaustive tests.
-short
フラグは長時間かかるようなテストをスキップするためのフラグです。
正直 go help testflag
のドキュメントを見てもよく分からなかったのですが、testingパッケージを見ると詳細がわかりました。
❯ go doc testing.Short
package testing // import "testing"
func Short() bool
Short reports whether the -test.short flag is set.
つまり、-short
フラグを指定しているとき、testing.Short()
がtrueになる挙動のようです。実際のユースケースとしては testing.Short()
がtrueの場合に、testing.Skip()
や testing.SkipNow()
を呼ぶことが多いようです。
実際に試してみます!
main_test.go
package main
import (
"testing"
)
func TestSumInt(t *testing.T) {
cases := map[string]struct {
input []int
expected int
}{
"empty": {
input: []int{},
expected: 0,
},
"result_15": {
input: []int{1, 2, 3, 4, 5},
expected: 15,
},
}
for name, c := range cases {
t.Run(name, func(t *testing.T) {
if len(c.input) == 0 && testing.Short() {
t.Skip("SKIP")
}
actual := SumInt(c.input)
if actual != c.expected {
t.Errorf("expected %d, got %d", c.expected, actual)
}
})
}
}
まずは -short
フラグなしで実行してみます。
❯ go test . -v
=== RUN TestSumInt
=== RUN TestSumInt/empty
=== RUN TestSumInt/result_15
--- PASS: TestSumInt (0.00s)
--- PASS: TestSumInt/empty (0.00s)
--- PASS: TestSumInt/result_15 (0.00s)
PASS
ok github.com/k3forx/testflag 0.266s
すべてのサブテストが実行されてますね!
次は -short
フラグを指定してみます。
❯ go test . -v -short
=== RUN TestSumInt
=== RUN TestSumInt/empty
main_test.go:25: SKIP
=== RUN TestSumInt/result_15
--- PASS: TestSumInt (0.00s)
--- SKIP: TestSumInt/empty (0.00s)
--- PASS: TestSumInt/result_15 (0.00s)
PASS
ok github.com/k3forx/testflag 0.172s
「SKIP」というログと共に TestSumInt/empty
がスキップされることが確かめられました!
-shuffle
18. シグネチャと詳細は以下になります。
-shuffle off,on,N
Randomize the execution order of tests and benchmarks.
It is off by default. If -shuffle is set to on, then it will seed
the randomizer using the system clock. If -shuffle is set to an
integer N, then N will be used as the seed value. In both cases,
the seed will be reported for reproducibility.
-shuffle
フラグを指定するとテストとベンチマークの実行順序をランダムにできます。 デフォルトではオフになっています。オンになっている場合、system clockを用いて乱数の初期化が行われるようです。もし整数 N
に対して -shuffle
フラグが指定されている場合には、N
がseed値として使用されるようです。
実際に確かめてみます!
main_test.go
package main
import (
"testing"
)
func TestSum1(t *testing.T) {
nums := []int{1, 2, 3, 4, 5}
expected := 15
if acutal := Sum(nums); acutal != expected {
t.Errorf("Expected %d but got %d", expected, acutal)
}
}
func TestSum2(t *testing.T) {
nums := []int{}
expected := 0
if acutal := Sum(nums); acutal != expected {
t.Errorf("Expected %d but got %d", expected, acutal)
}
}
-shuffle
フラグなしだと TestSum1
の後に TestSum2
が実行されます。
❯ go test . -v
=== RUN TestSum1
--- PASS: TestSum1 (0.00s)
=== RUN TestSum2
--- PASS: TestSum2 (0.00s)
PASS
ok github.com/k3forx/testflag 0.158s
-shuffle
フラグを指定して実行してみます!
❯ go test . -shuffle on -v
-test.shuffle 1729606565255227000
=== RUN TestSum2
--- PASS: TestSum2 (0.00s)
=== RUN TestSum1
--- PASS: TestSum1 (0.00s)
PASS
ok github.com/k3forx/testflag 0.163s
TestSum2
のあとに TestSum1
が実行されてますね!
-skip
19. シグネチャと詳細は以下になります。
-skip regexp
Run only those tests, examples, fuzz tests, and benchmarks that
do not match the regular expression. Like for -run and -bench,
for tests and benchmarks, the regular expression is split by unbracketed
slash (/) characters into a sequence of regular expressions, and each
part of a test's identifier must match the corresponding element in
the sequence, if any.
-skip
フラグは正規表現にマッチしないテストを実行する(マッチするテストをスキップする) ためのフラグです。-run
や -bench
と同じように正規表現は /
で区切られて適用されます。
実際に試してみます!
main_test.go
package main
import (
"testing"
)
func TestSumInt(t *testing.T) {
cases := map[string]struct {
input []int
expected int
}{
"empty": {
input: []int{},
expected: 0,
},
"result_15": {
input: []int{1, 2, 3, 4, 5},
expected: 15,
},
}
for name, c := range cases {
t.Run(name, func(t *testing.T) {
actual := SumInt(c.input)
if actual != c.expected {
t.Errorf("expected %d, got %d", c.expected, actual)
}
})
}
}
empty
のサブテストをスキップするために -skip "/empty"
を指定してみます。
❯ go test . -v -skip "/empty"
=== RUN TestSumInt
=== RUN TestSumInt/result_15
--- PASS: TestSumInt (0.00s)
--- PASS: TestSumInt/result_15 (0.00s)
PASS
ok github.com/k3forx/testflag 0.259s
ちゃんとスキップされていそうです!
-timeout
20. シグネチャと詳細は以下になります。
-timeout d
If a test binary runs longer than duration d, panic.
If d is 0, the timeout is disabled.
The default is 10 minutes (10m).
-timeout
フラグを指定すると、指定された時間 d
よりテストの実行時間が長い場合、パニックするようになっています。d
が 0
の場合には、タイムアウトはしないようになっており、デフォルトでは 10 (10m)
が指定されています。
実際に確かめてみます!
package main
import (
"testing"
"time"
)
func TestSum(t *testing.T) {
nums := []int{1, 2, 3, 4, 5}
time.Sleep(10 * time.Second)
expected := 15
if acutal := Sum(nums); acutal != expected {
t.Errorf("Expected %d but got %d", expected, acutal)
}
}
time.Sleep
で10秒間待つようにしているので、-timeout
フラグとして 1s
を指定します。
❯ go test . -timeout 1s
panic: test timed out after 1s
running tests:
TestSum (1s)
goroutine 21 [running]:
testing.(*M).startAlarm.func1()
/Users/XXX/.asdf/installs/golang/1.23.2/go/src/testing/testing.go:2373 +0x30c
created by time.goFunc
/Users/XXX/.asdf/installs/golang/1.23.2/go/src/time/sleep.go:215 +0x38
goroutine 1 [chan receive]:
testing.(*T).Run(0x140000b64e0, {0x102938930?, 0x140000a0b48?}, 0x1029a6ea0)
/Users/XXX/.asdf/installs/golang/1.23.2/go/src/testing/testing.go:1751 +0x328
testing.runTests.func1(0x140000b64e0)
/Users/XXX/.asdf/installs/golang/1.23.2/go/src/testing/testing.go:2168 +0x40
testing.tRunner(0x140000b64e0, 0x140000a0c68)
/Users/XXX/.asdf/installs/golang/1.23.2/go/src/testing/testing.go:1690 +0xe4
testing.runTests(0x140000c0030, {0x102a77ff0, 0x1, 0x1}, {0x8400000000000000?, 0x845f8d6b25824c05?, 0x102a81580?})
/Users/XXX/.asdf/installs/golang/1.23.2/go/src/testing/testing.go:2166 +0x3ac
testing.(*M).Run(0x140000a60a0)
/Users/XXX/.asdf/installs/golang/1.23.2/go/src/testing/testing.go:2034 +0x588
main.main()
_testmain.go:45 +0x90
goroutine 20 [sleep]:
time.Sleep(0x2540be400)
/Users/XXX/.asdf/installs/golang/1.23.2/go/src/runtime/time.go:315 +0xe0
github.com/k3forx/testflag.TestSum(0x140000b6680)
/Users/XXX/repos/scrap/testflag/main_test.go:11 +0x58
testing.tRunner(0x140000b6680, 0x1029a6ea0)
/Users/XXX/.asdf/installs/golang/1.23.2/go/src/testing/testing.go:1690 +0xe4
created by testing.(*T).Run in goroutine 1
/Users/XXX/.asdf/installs/golang/1.23.2/go/src/testing/testing.go:1743 +0x314
FAIL github.com/k3forx/testflag 1.198s
FAIL
確かにパニックしていそうです!test timed out after 1s
というログも表示されていますね!
-v
21. シグネチャと詳細は以下になります。
-v
Verbose output: log all tests as they are run. Also print all
text from Log and Logf calls even if the test succeeds.
-v
フラグを付与すると冗長な(verboseな)outputが表示されます。テストが成功していても Log
や Logf
で指定されたテキストが表示されます。
実際に確かめてみます!
package main
import (
"testing"
)
func TestSum(t *testing.T) {
nums := []int{1, 2, 3, 4, 5}
t.Log("Given the array of numbers: ", nums)
expected := 15
t.Logf("Expected: %d", expected)
if acutal := Sum(nums); acutal != expected {
t.Errorf("Expected %d but got %d", expected, acutal)
}
}
-v
フラグありとなしでテストを実行してみます。
❯ go test .
ok github.com/k3forx/testflag 0.237s
❯ go test . -v
=== RUN TestSum
main_test.go:9: Given the array of numbers: [1 2 3 4 5]
main_test.go:12: Expected: 15
--- PASS: TestSum (0.00s)
PASS
ok github.com/k3forx/testflag 0.140s
-v
フラグを付与すると t.Log
や t.Logf
で指定されたメッセージも合わせて表示されました!
-vet
22. シグネチャと詳細は以下になります。
-vet list
Configure the invocation of "go vet" during "go test"
to use the comma-separated list of vet checks.
If list is empty, "go test" runs "go vet" with a curated list of
checks believed to be always worth addressing.
If list is "off", "go test" does not run "go vet" at all.
-vet
フラグは go test
中に実行する go vet
の呼び出しを設定するためのフラグです。カンマで分割したリストのvetチェックが実行されます。リストが空の場合、go test
は go vet
を実行し、常に取り組む価値があると思われるチェックを厳選して実行します。もしリストは off
で指定されている場合、go test
は go vet
を実行しないようになっています。
実際に確かめてみます!
appends
のvetチェックに引っ掛かるように main.go
を記述してみます。
appends
については go tool vet help appends
を実行すると詳細がわかります。
main.go
package main
func AppendInt(nums []int, n int) []int {
s := []string{"a", "b", "c"}
_ = append(s)
return append(nums, n)
}
main_test.go
package main
import (
"slices"
"testing"
)
func TestAppendInt(t *testing.T) {
nums := []int{1, 2, 3}
n := 4
expected := []int{1, 2, 3, 4}
actual := AppendInt(nums, n)
if !slices.Equal(actual, expected) {
t.Errorf("expected %v but got %v", expected, actual)
}
}
-vet
フラグなしで実行してみると通常通りテストが通ります。
❯ go test .
ok github.com/k3forx/testflag 0.229s
-vet
フラグありだとテストが落ちるようになっていますね!
❯ go test . -vet appends
# github.com/k3forx/testflag
# [github.com/k3forx/testflag]
./main.go:17:6: append with no values
FAIL github.com/k3forx/testflag [build failed]
FAIL
-vet
フラグが受け付けるリストは go tool vet help
で確かめられます!
テストの実行中にテスト自体をプロファイルするフラグ
-benchmem
23. シグネチャと詳細は以下になります。
-benchmem
Print memory allocation statistics for benchmarks.
Allocations made in C or using C.malloc are not counted.
-benchmem
フラグはベンチマークテストに対するメモリのアロケーションの統計を表示してくれるフラグです。CやC.mallocを通じてのアロケーションはカウントされません。
実際に試してみます!
package main
import "testing"
func BenchmarkTest(b *testing.B) {
nums := []int{1, 2, 3, 4, 5}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = Sum(nums)
}
}
-benchmem
フラグをつけて実行してみます!
❯ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/k3forx/testflag
cpu: Apple M3 Pro
BenchmarkSum-11 315525358 3.691 ns/op 0 B/op 0 allocs/op
PASS
ok github.com/k3forx/testflag 1.778s
最後の allocs/op
の部分がメモリアロケーションに関する統計を示してます。
-blockprofile
24. シグネチャと詳細は以下になります。
-blockprofile block.out
Write a goroutine blocking profile to the specified file
when all tests are complete.
Writes test binary as -c would.
-blockprofile
フラグはすべてのテストが完了した後、goroutineのブロッキングプロファイルを指定されたファイルに書き込むフラグです。-c
フラグが行うようにテストバイナリを書き込みます。
実際に試してみたいと思います!
❯ go test -bench . -blockprofile block.out
goos: darwin
goarch: arm64
pkg: github.com/k3forx/testflag
cpu: Apple M3 Pro
BenchmarkSum-11 335249805 3.609 ns/op
PASS
ok github.com/k3forx/testflag 1.577s
実行すると以下の2つのファイルが生成されていると思います。
testflag.test
block.out
❯ ls
block.out go.mod main.go main_test.go testflag.test
testflag.test
がテストのバイナリファイルで、block.out
がgoroutineのブロッキングプロファイルになります。得られたプロファイルのバイナリファイルは go tool pprof
コマンドを使用すると可視化できます。詳細は go tool pprof -h
を参照してください。
-blockprofilerate
25. シグネチャと詳細は以下になります。
-blockprofilerate n
Control the detail provided in goroutine blocking profiles by
calling runtime.SetBlockProfileRate with n.
See 'go doc runtime.SetBlockProfileRate'.
The profiler aims to sample, on average, one blocking event every
n nanoseconds the program spends blocked. By default,
if -test.blockprofile is set without this flag, all blocking events
are recorded, equivalent to -test.blockprofilerate=1.
-blockprofilerate
フラグはgoroutineのブロッキングプロファイルによって提供される詳細を、 runtime.SetBlockProfileRate(n)
によってコントロールするためのフラグです。プロファイラーは、平均して、プログラムがブロックされている時間 n
ナノ秒ごとに1つのブロッキング・イベントをサンプリングすることを目標としているようです。
より詳細は go doc runtime.SetBlockProfileRate
でみることができます。
❯ go doc runtime.SetBlockProfileRate
package runtime // import "runtime"
func SetBlockProfileRate(rate int)
SetBlockProfileRate controls the fraction of goroutine blocking events that
are reported in the blocking profile. The profiler aims to sample an average
of one blocking event per rate nanoseconds spent blocked.
To include every blocking event in the profile, pass rate = 1. To turn off
profiling entirely, pass rate <= 0.
実際に試してみたいと思います!
まずは -blockprofilerate 1
としてみます。
❯ go test -bench . -blockprofile block.out -blockprofilerate 1
goos: darwin
goarch: arm64
pkg: github.com/k3forx/testflag
cpu: Apple M3 Pro
BenchmarkSum-11 482151358 2.244 ns/op
PASS
ok github.com/k3forx/testflag 1.541s
❯ go tool pprof block.out
File: testflag.test
Type: delay
Time: Oct 30, 2024 at 5:51pm (JST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top3
Showing nodes accounting for 1.34s, 100% of 1.34s total
Dropped 4 nodes (cum <= 0.01s)
Showing top 3 nodes out of 11
flat flat% sum% cum cum%
1.34s 100% 100% 1.34s 100% runtime.chanrecv1
0 0% 100% 1.34s 100% main.main
0 0% 100% 1.34s 100% runtime.main
(pprof)
確かにプロファイルが取得できていますね!
続いて -blockprofilerate 0
のケースを試してみます。
❯ go test -bench . -blockprofile block.out -blockprofilerate 0
goos: darwin
goarch: arm64
pkg: github.com/k3forx/testflag
cpu: Apple M3 Pro
BenchmarkSum-11 523895547 2.244 ns/op
PASS
ok github.com/k3forx/testflag 1.491s
❯ go tool pprof block.out
File: testflag.test
Type: delay
Time: Oct 30, 2024 at 5:55pm (JST)
No samples were found with the default sample value type.
Try "sample_index" command to analyze different sample values.
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)
「No samples were found with the default sample value type.」と表示されているので、サンプリンが行われていないことがわかります!
-coverprofile
26. シグネチャと詳細は以下になります。
-coverprofile cover.out
Write a coverage profile to the file after all tests have passed.
Sets -cover.
-coverprofile
フラグはすべてのテストが通った後で、カバレッジのプロファイルをファイルに書き込むフラグです。-coverprofile
フラグを指定している場合は、-cover
フラグは不要になります。
実際に確かめてみます!
Sum
関数に対する以下のテストコードのカバレッジを見てみます。
❯ go test . -covermode set -coverprofile cover.out
ok github.com/k3forx/testflag 0.234s coverage: 100.0% of statements
テストが通った場合、cover.out
に以下のようなカバレッジのプロファイルが書き込まれていることがわかると思います。可視化したい場合は go tool cover
を使います。
❯ cat cover.out
mode: set
github.com/k3forx/testflag/main.go:5.26,7.37 2 1
github.com/k3forx/testflag/main.go:7.37,9.3 1 1
github.com/k3forx/testflag/main.go:10.2,10.12 1 1
-covermode
フラグの引数の違いについても以下で詳細にみていきたいと思います。
各モードで違いが出るように以下のコードで試してみたいと思います。
main.go
package main
import (
"strconv"
)
func ToString(v any) string {
switch v.(type) {
case int:
return strconv.Itoa(v.(int))
case string:
return v.(string)
case bool:
return strconv.FormatBool(v.(bool))
default:
return ""
}
}
main_test.go
package main
import "testing"
func TestToString(t *testing.T) {
cases := map[string]struct {
v any
expected any
}{
"string_hello": {
v: "hello",
expected: "hello",
},
"string_empty": {
v: "",
expected: "",
},
"true": {
v: true,
expected: "true",
},
}
for name, c := range cases {
t.Run(name, func(t *testing.T) {
t.Parallel()
if actual := ToString(c.v); actual != c.expected {
t.Errorf("Expected %v but got %v", c.expected, actual)
}
})
}
}
-covermode set
で実行してみる
26.1 ❯ go test . -covermode set -coverprofile set.out
ok github.com/k3forx/testflag 0.198s coverage: 60.0% of statements
go tool cover -html=set.out
で可視化してみると以下のような表示が得られます。
return strconv.Itoa(v.(int))
と return ""
の部分はテストされていないことがわかりますね!
-covermode count
で実行してみる
26.2 ❯ go test . -covermode count -coverprofile count.out
ok github.com/k3forx/testflag 0.207s coverage: 60.0% of statements
go tool cover -html=count.out
で可視化してみると以下のような表示が得られます。
-covermode set
でみたのと同じように return strconv.Itoa(v.(int))
と return ""
の部分はテストされていないことがわかります。
さらに return v.(string)
と return strconv.FormatBool(v.(bool))
は微妙に色が違っていることがわかると思います。これはテストで実行された回数に依存していて、色が濃くなるほどテストで実行された回数が多いことを示します。
-covermode atomic
で実行してみる
26.3 ❯ go test . -covermode atomic -coverprofile atomic.out
ok github.com/k3forx/testflag 0.268s coverage: 60.0% of statements
go tool cover -html=atomic.out
で可視化してみると以下のような表示が得られます。
こちらの図は -covermode count
を指定して得られた図とまったく同じになってしまいました。
今回のテストケースでは -covermode count
を指定した場合と -covermode atomic
を指定した場合で差異はないということになりますね!
-cpuprofile
27. シグネチャと詳細は以下になります。
-cpuprofile cpu.out
Write a CPU profile to the specified file before exiting.
Writes test binary as -c would.
-cpuprofile
フラグはCPUのプロファイルを指定されたファイルに書き込むためのフラグです。
実際に試してみます!(実行されたテストがわかりやすいように -v
フラグもつけています)
❯ go test . -cpuprofile cpu.out -v
=== RUN TestSum
--- PASS: TestSum (0.00s)
PASS
ok github.com/k3forx/testflag 0.283s
テストを実行すると cpu.out
というファイルが生成されます。
❯ ls | grep cpu.out
cpu.out
詳細を見ようとすると以下のように「No samples were found with the default sample value type.」という表示が出てきます。これはプログラムの実行が早すぎてプロファイルを取れなかったことを意味しています。CPUプロファイルを取る場合はベンチマークのテストを実行して、プロファイルを取ることも多いようです。
❯ go tool pprof cpu.out
File: testflag.test
Type: cpu
Time: Oct 29, 2024 at 9:39pm (JST)
Duration: 202.24ms, Total samples = 0
No samples were found with the default sample value type.
Try "sample_index" command to analyze different sample values.
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)
-memprofile
28. シグネチャと詳細は以下になります。
-memprofile mem.out
Write an allocation profile to the file after all tests have passed.
Writes test binary as -c would.
-memprofile
フラグはすべてのテストが成功した後に、メモリアロケーションのプロファイルを指定されたファイルに書き込むためのフラグです。
実際に試してみます!(実行されたテストがわかりやすいように -v
フラグもつけています)
❯ go test . -memprofile mem.out -v
=== RUN TestSum
--- PASS: TestSum (0.00s)
PASS
ok github.com/k3forx/testflag 0.082s
テストを実行すると mem.out
というファイルが生成されます。
❯ ls | grep mem.out
mem.out
詳細を見ようとすると以下のように No samples were found with the default sample value type.
という表示が出てきます。これはプログラムの実行が早すぎてプロファイルを取れなかったことを意味しています。メモリのプロファイルを取る場合はベンチマークのテストを実行して、プロファイルを取ることも多いようです。
❯ go tool pprof mem.out
File: testflag.test
Type: alloc_space
Time: Oct 29, 2024 at 9:55pm (JST)
No samples were found with the default sample value type.
Try "sample_index" command to analyze different sample values.
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)
-memprofilerate
29. シグネチャと詳細は以下になります。
-memprofilerate n
Enable more precise (and expensive) memory allocation profiles by
setting runtime.MemProfileRate. See 'go doc runtime.MemProfileRate'.
To profile all memory allocations, use -test.memprofilerate=1.
-memprofile
フラグは、runtime.MemProfileRate
で指定される値によって、より正確で (かつコストのかかる) メモリアロケーションのプロファイルを取得するためのフラグです。すべてのメモリアロケーションをサンプリングするには 1
を指定します。
ちなみに go doc runtime.MemProfileRate
を実行すると以下のように詳細を得ることができます。
❯ go doc runtime.MemProfileRate
package runtime // import "runtime"
var MemProfileRate int = 512 * 1024
MemProfileRate controls the fraction of memory allocations that are recorded
and reported in the memory profile. The profiler aims to sample an average
of one allocation per MemProfileRate bytes allocated.
To include every allocated block in the profile, set MemProfileRate to 1.
To turn off profiling entirely, set MemProfileRate to 0.
The tools that process the memory profiles assume that the profile rate is
constant across the lifetime of the program and equal to the current value.
Programs that change the memory profiling rate should do so just once,
as early as possible in the execution of the program (for example, at the
beginning of main).
実際に試してみます!
-memprofilerate=1
として実行してみます。
❯ go test -bench . -memprofile mem.out -v -memprofilerate=1
goos: darwin
goarch: arm64
pkg: github.com/k3forx/testflag
cpu: Apple M3 Pro
BenchmarkSum
BenchmarkSum-11 315554538 3.690 ns/op
PASS
ok github.com/k3forx/testflag 1.809s
go tool pprof
を使ってみると、確かにプロファイルが取得できていることがわかると思います。
❯ go tool pprof mem.out
File: testflag.test
Type: alloc_space
Time: Oct 29, 2024 at 10:06pm (JST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top10
Showing nodes accounting for 101.70kB, 95.84% of 106.12kB total
Dropped 70 nodes (cum <= 0.53kB)
Showing top 10 nodes out of 57
flat flat% sum% cum cum%
72kB 67.85% 67.85% 72kB 67.85% regexp.(*bitState).reset
9.06kB 8.54% 76.39% 9.06kB 8.54% sync.(*Pool).pinSlow
7kB 6.60% 82.99% 7kB 6.60% runtime.malg
4.50kB 4.24% 87.23% 4.50kB 4.24% runtime.makeProfStackFP (inline)
4kB 3.77% 91.00% 10.25kB 9.66% runtime.allocm
1.84kB 1.74% 92.73% 1.84kB 1.74% testing.(*B).ResetTimer
1.16kB 1.09% 93.82% 44.57kB 42.00% testing.(*B).Run
0.81kB 0.77% 94.59% 0.81kB 0.77% fmt.init.func1
0.70kB 0.66% 95.25% 86.93kB 81.92% testing.runBenchmarks
0.62kB 0.59% 95.84% 1.25kB 1.18% testing.runTests
(pprof)
続いて、-memprofilerate=0
として実行したのちに、go tool pprof
を使ってプロファイルを見てみます。
❯ go test -bench . -memprofile mem.out -v -memprofilerate=0
goos: darwin
goarch: arm64
pkg: github.com/k3forx/testflag
cpu: Apple M3 Pro
BenchmarkSum
BenchmarkSum-11 317048524 3.728 ns/op
PASS
ok github.com/k3forx/testflag 1.645s
❯ go tool pprof mem.out
File: testflag.test
Type: alloc_space
Time: Oct 29, 2024 at 10:08pm (JST)
No samples were found with the default sample value type.
Try "sample_index" command to analyze different sample values.
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)
「No samples were found with the default sample value type.」と記述があるようにプロファイルは取得できていないことがわかります。
-mutexprofile
30. シグネチャと詳細は以下になります。
-mutexprofile mutex.out
Write a mutex contention profile to the specified file
when all tests are complete.
Writes test binary as -c would.
-mutexprofile
フラグはすべてのテストが完了したとき、ミューテックスの競合に関するプロファイルを指定されたファイルに書き込むフラグです。
実際に試してみます!
❯ go test . -mutexprofile mutex.out -bench .
goos: darwin
goarch: arm64
pkg: github.com/k3forx/testflag
cpu: Apple M3 Pro
BenchmarkSum-11 479839014 2.237 ns/op
PASS
ok github.com/k3forx/testflag 1.558s
mutex.out
というファイルが作成されていることが確かめられると思います。
実際に可視化してみたい場合は go tool pprof
コマンドを使用します。
-mutexprofilefraction
31. シグネチャと詳細は以下になります。
-mutexprofilefraction n
Sample 1 in n stack traces of goroutines holding a
contended mutex.
-mutexprofilefraction
フラグは、競合するミューテックスを保持するゴルーチンのスタックトレースn個に1個のサンプリングを行うフラグです。
-memprofilerate
フラグと同様に go doc runtime.SetMutexProfileFraction
をみるとより詳細を知ることができます。
❯ go doc runtime.SetMutexProfileFraction
package runtime // import "runtime"
func SetMutexProfileFraction(rate int) int
SetMutexProfileFraction controls the fraction of mutex contention events
that are reported in the mutex profile. On average 1/rate events are
reported. The previous rate is returned.
To turn off profiling entirely, pass rate 0. To just read the current rate,
pass rate < 0. (For n>1 the details of sampling may change.)
ミューテックスのプロファイルで報告されたミューテックスの競合のうちの 1/n
がサンプリングされるようです。0
を渡せばプロファイルをオフにでき、1
を渡せばすべてサンプリングするようです。
実際に試してみます!
-mutexprofilefraction
フラグの値として 1
を指定してみます。
❯ go test . -mutexprofile mutex.out -bench . -mutexprofilefraction 1
goos: darwin
goarch: arm64
pkg: github.com/k3forx/testflag
cpu: Apple M3 Pro
BenchmarkSum-11 517613786 2.237 ns/op
PASS
ok github.com/k3forx/testflag 1.477s
❯ go tool pprof mutex.out
File: testflag.test
Type: delay
Time: Oct 29, 2024 at 10:50pm (JST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top3
Showing nodes accounting for 346.34us, 100% of 346.34us total
flat flat% sum% cum cum%
346.34us 100% 100% 346.34us 100% runtime._LostContendedRuntimeLock
(pprof)
続いて -mutexprofilefraction
フラグの値として 0
を指定してみます
❯ go test . -mutexprofile mutex.out -bench . -mutexprofilefraction 0
goos: darwin
goarch: arm64
pkg: github.com/k3forx/testflag
cpu: Apple M3 Pro
BenchmarkSum-11 519822098 2.243 ns/op
PASS
ok github.com/k3forx/testflag 1.483s
❯ go tool pprof mutex.out
File: testflag.test
Type: delay
Time: Oct 29, 2024 at 10:51pm (JST)
No samples were found with the default sample value type.
Try "sample_index" command to analyze different sample values.
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)
「No samples were found with the default sample value type.」という表示があるので、確かにサンプリングされていないことが分かりますね。
-outputdir
32. シグネチャと詳細は以下になります。
-outputdir directory
Place output files from profiling in the specified directory,
by default the directory in which "go test" is running.
-outputdir
フラグはプロファイリングの結果を指定されたディレクトリに置くためのコマンドです。デフォルトでは go test
が実行されたディレクトリにファイルが置かれます。
実際に試してみます!
❯ go test . -memprofile mem.out -outputdir ./profile
ok github.com/k3forx/testflag 0.003s
❯ tree ./profile
./profile
└── mem.out
1 directory, 1 file
ちなみに -outputdir
フラグで指定するディレクトリは先に作成しておく必要があります。
-trace
33. シグネチャと詳細は以下になります。
-trace trace.out
Write an execution trace to the specified file before exiting.
-trace
フラグは実行トレースを指定されたファイルに書き込むためのフラグです。
実際に試してみます!
❯ go test . -trace trace.out
ok github.com/k3forx/testflag 0.246s
trace.out
というファイルが作成されていると思います!
❯ ls | grep trace.out
trace.out
可視化をしたい場合は go tool trace
コマンドを使用します。可視化されたtraceの見方については、テストのフラグの本筋とは離れてしまうので機会があれば解説記事を書いてみたいと思います!
まとめ
go test
に指定できる全33個のフラグをすべて解説してみました。知らないフラグもたくさんあったのではないでしょうか?
Goでテストを書く際の参考になれば幸いです!
Discussion