【Go语言入门教程】单元测试时同一个包下不同文件函数调用报错为 `undefined`

本期目录
单元测试时同一个包下不同文件函数调用报错为 undefined
的问题
1. 背景知识
在 Terminal 终端命令行中,使用 go test
指令执行单元测试,所有以 _test.go
结尾的代码内以 Test
开头的函数会自动被执行。
go test -v hello_test.go
其中,-v
可以让测试时显示详细的流程信息。
2. 问题描述
单元测试的目录结构如下图所示:
① hello.go
代码:
package hello
func Hello() string {
return "Hello, world."
}
② hello_test.go
代码:
package hello
import "testing"
func TestHello(t *testing.T) {
want := "Hello, world."
if got := Hello(); got != want {
t.Errorf("Hello() = %q, want %q", got, want)
}
}
测试代码第 7 行中,调用了源代码的 Hello()
函数。如果直接运行单元测试,则测试不通过:
提示我们,undefined: Hello
。即 Hello
函数未定义。
3. 原因
【原因】出现上述错误很大可能是当前项目运行在 GOPATH 模式下。在 GOPATH 模式下,go test 会为指定的源码文件生成一个虚拟代码包 “command-line-arguments”,而 hello_test.go
调用了 hello.go
中的 Hello()
函数并不属于代码包 “command-line-arguments” ,编译不通过,错误自然就产生了。
因此解决方式有两种:
- 放弃使用 GOPATH 模式,改用 go mod 模式 (实际开发中使用最多,依赖管理很方便) 。
- 坚持使用 GOPATH 模式,更改编译指令。
4. 解决方法一:关闭GOPATH,使用go mod
4.1 设置GO111MODULE
Win + R 输入 cmd 打开命令行,输入:
go env
即可看到 GO111MODULE (默认情况是空的):
GO111MODULE 有三个值:off、on 和 auto (默认值)
GO111MODULE=off
:go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找。GO111MODULE=on
:go命令行会使用modules,而一点也不会去GOPATH目录下查找。GO111MODULE=auto
:默认值,go命令行将会根据当前目录来决定是否启用module功能。这种情况下可以分为两种情形:- 当前目录在GOPATH/src之外且该目录包含go.mod文件
- 当前文件在包含go.mod文件的目录下面。
【注】
- 在使用go module时,GOPATH是无意义的。不过它仍然会把下载的依赖存储在
$GOPATH/pkg/mod
中,也会把go install
的结果放在$GOPATH/bin
中。 - 当module功能启用时,依赖包的存放位置变更为
$GOPATH/pkg
。允许同一个package多个版本并存,且多个项目可以共享缓存的module。
设置的命令如下:
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
可在命令行中输入:go env
查看 GO111MODULE=on
。
4.2 清空所有GOPATH
开启 go mod 之后,并不能与 GOPATH 共存。必须把项目从 GOPATH 中移除,否则会报 $GOPATH/go.mod exists but should not
的错误。
在 Goland 中,移除项目所有 GOPATH 的操作如下:
清空 GOPATH 之后,在单元测试模式下,同一个包下不同文件函数调用报错为 undefined
的问题也会解决。
4.3 在新项目中创建go mod
打开Windows终端命令行,cd
到新项目的文件夹目录。输入命令:
go mod init XXX(你的文件夹名称)
成功创建 go.mod
文件如下所示:
4.4 执行单元测试
在所有相关的 .go
文件的同一个目录下 (在本例中,hello_test.go
和被调用的 hello.go
都在同一个目录 src
下),直接输入指令:
$ go test
//或
$ go test -v
即可直接执行测试:
单元测试成功。
5. 解决方法二:坚持GOPATH模式,改变测试命令
如果你有特殊原因仍然坚持使用 GOPATH 模式,解决方案如下:
使用 Terminal 命令行的 go test
指令,加上要调用的文件 hello.go
。如下代码所示:
//进入测试文件所在的目录
$ cd src
//执行整个测试文件和调用到的文件
$ go test -v hello_test.go hello.go
输出:
=== RUN TestHello
--- PASS: TestHello (0.00s)
PASS
ok command-line-arguments 0.054s
测试成功。