How PouchContainer Achieves Full Integration Test Coverage with Go
This article explains how Alibaba's open‑source PouchContainer adds both unit‑test and integration‑test coverage reporting using Go's cover tool, TravisCI, Codecov, and custom test harnesses, providing step‑by‑step commands, code snippets, and PR details to raise overall coverage from 18% to 60%.
Go test coverage basics
Go measures coverage by rewriting source files before compilation and inserting counters that record which statements are executed. The coverage tool can be invoked with the -cover flag and the results are written to a profile file specified by -coverprofile. The first line of the profile indicates the mode ( set, count or atomic); subsequent lines have the format
file.go:startLine.startCol,endLine.endCol numberOfStatements count.
Example function:
package size
func Size(a int) string {
switch {
case a < 0:
return "negative"
case a == 0:
return "zero"
case a < 10:
return "small"
}
return "enormous"
}Corresponding test:
package size
import (
"fmt"
"testing"
)
type Test struct {
in int
out string
}
var tests = []Test{{-1, "negative"}, {5, "small"}}
func TestSize(t *testing.T) {
fmt.Println("a")
for i, test := range tests {
size := Size(test.in)
if size != test.out {
t.Errorf("#%d: Size(%d)=%s; want %s", i, test.in, size, test.out)
}
}
}Running the test with coverage:
go test -x -cover -coverprofile=./size.outThe -x flag prints the execution steps, -cover enables coverage collection, and -coverprofile specifies the output file. The final line of the output shows something like coverage: 60.0% of statements.
Unit‑test coverage collection
Collecting unit‑test coverage is performed by the Makefile target:
make unit-testThe Makefile excludes irrelevant packages such as vendor and types to keep the metric accurate.
Integration‑test coverage collection
Integration tests exercise the Pouch daemon via the CLI or API. Because the daemon binary is normally built without instrumentation, additional steps are required to collect coverage:
Add a main_test.go file at the repository root.
Extend the build script ( hack/build) with a testserver function that compiles the main package into a test binary.
In hack/make.sh, launch the generated test binary in the background and run the API/CLI tests against it.
After the tests finish, send a termination signal to the test process and collect the coverage profile.
The main_test.go defines a TestMain function that filters out the -test and DEVEL flags from os.Args, starts the daemon in a goroutine, and waits for either a termination signal (SIGINT, SIGTERM, etc.) or the daemon’s completion channel. This prevents the daemon from re‑invoking the test binary and ensures a clean exit.
package main
import (
"os"
"os/signal"
"strings"
"syscall"
"testing"
)
func TestMain(t *testing.T) {
var args []string
for _, arg := range os.Args {
switch {
case strings.HasPrefix(arg, "DEVEL"):
case strings.HasPrefix(arg, "-test"):
default:
args = append(args, arg)
}
}
waitCh := make(chan int, 1)
os.Args = args
go func() { main(); close(waitCh) }()
signalCh := make(chan os.Signal, 1)
signal.Notify(signalCh, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGHUP)
select {
case <-signalCh:
return
case <-waitCh:
return
}
}Compile the instrumented test binary without running it:
# go test -c -race -cover -covermode=atomic -o pouchd-test -coverpkg $pkgsRun the integration‑test binary while collecting coverage:
# pouchd-test -test.coverprofile=$DIR/integrationcover.out DEVEL --debugThe -test prefix is processed by the go test driver; the DEVEL flag and the following arguments are passed directly to the daemon’s main function. When the daemon exits, go test prints the coverage summary and writes the profile file.
The implementation is described in the pull request https://github.com/alibaba/pouch/pull/1338, which adds the test harness, modifies the build scripts, and ensures the coverage data is generated.
After adding integration‑test coverage, PouchContainer’s overall coverage increased from about 18 % (unit tests only) to roughly 60 %.
Continuous integration
PouchContainer integrates Travis CI with Codecov. Each CI run generates a coverage.txt file, which is uploaded to Codecov via a script referenced in .travis.yml. The script is invoked through the Makefile target TEST_FLAGS= make build-integration-test.
For additional background on generating coverage profiles for Go integration tests, see https://www.cyphar.com/blog/post/20170412-golang-integration-coverage.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Alibaba Cloud Native
We publish cloud-native tech news, curate in-depth content, host regular events and live streams, and share Alibaba product and user case studies. Join us to explore and share the cloud-native insights you need.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
