Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/go: Test helper code not found #32379

Closed
kstenerud opened this issue Jun 1, 2019 · 10 comments
Closed

cmd/go: Test helper code not found #32379

kstenerud opened this issue Jun 1, 2019 · 10 comments
Milestone

Comments

@kstenerud
Copy link

@kstenerud kstenerud commented Jun 1, 2019

What version of Go are you using (go version)?

$ go version
go version go1.12.5 linux/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/karl/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/karl/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/karl/tmp/testhelper/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build398422980=/tmp/go-build -gno-record-gcc-switches"

What did you do?

go.mod:

module github.com/user/outer

go 1.12

inner/something_test.go:

package inner

import (
	"testing"

	"github.com/user/outer/test"
)

func TestIs1(t *testing.T) {
	test.AssertIs1(t, 1)
}

inner/something.go:

package inner

test/dummy.go:

package test

test/test_helpers_test.go:

package test

import "testing"

func AssertIs1(t *testing.T, value int) {
	if value != 1 {
		t.Errorf("Value %v is not 1", value)
	}
}

$ go test

What did you expect to see?

I expected the tests to run without error.

What did you see instead?

./something_test.go:10:2: undefined: test.AssertIs1
@mvdan

This comment has been minimized.

Copy link
Member

@mvdan mvdan commented Jun 1, 2019

When you import a package, you only import its non-test code. This is true even if you're importing the package from a test file. I can't find an exact quote on this, though. The spec doesn't speak of this, because I think this is implemented in the Go tool via build tags.

@kstenerud

This comment has been minimized.

Copy link
Author

@kstenerud kstenerud commented Jun 1, 2019

OK, but then how do I stop my test code from building when I call go build?

@mvdan

This comment has been minimized.

Copy link
Member

@mvdan mvdan commented Jun 1, 2019

The files included in a package when built are the same files that are included in a package when you import it. So I'm not sure I see what you're trying to do.

It's normal to have a test helper/utility package, but often it's an internal package so that only the owner's packages can import it.

@kstenerud

This comment has been minimized.

Copy link
Author

@kstenerud kstenerud commented Jun 1, 2019

Normally, test code doesn't get compiled unless you call go test, at which point all *_test.go files get built as well.

What I want is to preserve this behavior and also be able to have common test code.

But the go tool seems to be one-or-the-other. If I put my common test code in a *_test.go file, it can't be imported by my other test code. If I put it in a regular *.go file, it gets compiled even when I'm not calling go test. For test code that takes a long time to build, this becomes a problem.

@davecheney

This comment has been minimized.

Copy link
Contributor

@davecheney davecheney commented Jun 1, 2019

Yes, that is how it works. In your case outer appears to be a test helper package containing simple asset methods. Rename the files in that package so they do not end in _test.go and the code you supplied in your initial report will work as expected.

@kstenerud

This comment has been minimized.

Copy link
Author

@kstenerud kstenerud commented Jun 1, 2019

That was just a toy project to illustrate the problem. My primary concern is with a bigger project with lots of test support code that I don't want compiling unless I'm actually running the tests.

@davecheney

This comment has been minimized.

Copy link
Contributor

@davecheney davecheney commented Jun 1, 2019

I think this is mostly a hypothetical question. From the outline you provided outer will be cached by go build/test.

I know the go team care about compilation time. I suggest you try and iff there is a problem, raise a new bug with the concrete details.

@bcmills bcmills changed the title Test helper code not found cmd/go: Test helper code not found Jun 3, 2019
@bcmills

This comment has been minimized.

Copy link
Member

@bcmills bcmills commented Jun 3, 2019

@kstenerud, I suspect that the error message you see is coming from the vet step rather than actually building the test. Can you verify what exit code go test actually returned?

(#29258 proposes to remove this extension mechanism, because it is confusing and can add overhead to builds even when it works.)

@bcmills

This comment has been minimized.

Copy link
Member

@bcmills bcmills commented Jun 3, 2019

@jayconrod

This comment has been minimized.

Copy link
Contributor

@jayconrod jayconrod commented Jun 7, 2019

I looked into this a bit. It's working as intended. go test -vet=off reports the same error. go list -test -deps ./inner also makes it clear that the non-test variant of github.com/suer/outer/test is imported.

+1 to what @mvdan and @davecheney said above. It is possible to define test-only exported definitions, but they're only visible to tests in the same package. If you need test-only definitions visible to other packages, they need to be regular exported definitions in a regular package that's only imported by tests.

Just to recap on the current semantics:

  • Three variants may be built for each package.
    • The package under test (includes .go sources without "_test.go" suffix)
    • The internal test package (includes .go sources from the package under test, plus files with the "_test.go" suffix that don't have the _test package name suffix. (The tests in this example fall in this category))
    • The external test package (includes .go sources with the _test package name suffix only)
  • There's also a synthetic main package that imports the internal and external test packages.
  • In the import graph rooted at pmain, anything that imports the package under test is recompiled to import the internal test package instead.

The last point is the source of a lot of bugs and complication. The cached copies of packages recompiled due to that rule aren't usable for other tests, which blows up the build time. I think any expansion of those semantics would be too expensive and too confusing.

@jayconrod jayconrod closed this Jun 7, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
5 participants
You can’t perform that action at this time.