Skip to content

Proposal: automatically set a "test" tag during "go test" #14668

Closed
@tsuna

Description

@tsuna

This is a proposal for a change to go test. The goal is to make it easier to enable conditional compilation of test-only code, which is useful to stub out code during testing, by introducing a build tag that is automatically set during go test, similarly to how the race tag is automatically added for builds that use the -race flag.

For example, let's say we have some code that uses the unix.Setns() system call, which is a Linux-specific privileged system call (usually requires root or some elevated privileges like CAP_SYS_ADMIN). One way to make such code testable independently of the platform and privilege level is to do:

var setNs = func(h handle) error {
    return unix.Setns(h.fd(), unix.CLONE_NEWNET)
}

This way unit tests can override the call by changing the layer of indirection that the global variable creates:

func TestNetNs(t *testing.T) {
    // Mock setNs
    oldSetNs := setNs
    setNs = func(fd handle) error {
        return nil
    }
    defer func() {
        setNs = oldSetNs
    }()

However this is a bit tedious to do, it creates an unnecessary level of indirection in the production code and clutters the test code. Another solution that addresses this is to use a build tag instead:

// in setns_linux.go

// +build !test

func setNs(h handle) error {
    return unix.Setns(h.fd(), unix.CLONE_NEWNET)
}
// in setns_test.go
func setNs(h handle) error {
    return nil
}

The downside is that now go test ./... doesn't work anymore, one needs to know to pass -tags test. In a large codebase, people choose to use different build tags to implement this mechanism, which leads to inconsistencies, unless Makefiles (or equivalent) are used and go test is not to be run "manually".

Another place where a built-in test tag could be useful is when factoring out test libraries. Code in foo_test.go files is only visible in the current package and can't be imported from one package to another. Therefore it's common to create a test package to factor out test code re-used across packages. Doing this unfortunately creates room for production code importing test code. By flagging the test code with a build tag, this possibility is significantly reduced. But again, the build tag creates a usability issue.

There are a number of packages that already use a test tag (see for example the GitHub search for language:go "// +build test" filename:_test.go). Some of these packages would benefit from the usability improvement (for example golang.org/x/text does not use a Makefile but unicode/norm/forminfo_test.go assumes the test tag is used), other packages would start behaving differently (for example github.com/netrack/netrack-server would start running both the integration tests and the unit tests when make integration-test is run, instead of just the integration tests as the intent of the Makefile appears to be).

Alternatively the built-in tag could be gotest, which doesn't seem to be used on GitHub.

Looking forward to your feedback on this small proposal. Thanks in advance.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions