引言
终于开始学习golang细节了,此系列笔记旨在记录学习路上遇到的一些坑,还有一些感觉需要特别注意的知识点
环境准备
IDE:goland
go version: go version go1.14.1 darwin/amd64
遇到问题
(一)运行 go test 报错 go: cannot find main module; see 'go help modules'
- 问题分析:其根本原因是我使用了
go mod
进行依赖管理,然而使用项目根目录没有go.mod文件 - 解决办法:添加go.mod 文件,方法有二
(1)在目录中运行 go mod init
(2)如果使用的是goland ide,可以通过配置完成https://goproxy.io,direct
知识点
(一)如何编写单元测试:
编写测试和函数很类似,其中有一些规则
- 程序需要在一个名为
xxx_test.go
的文件中编写 - 测试函数的命名必须以单词
Test
开始 - 测试函数只接受一个参数
t *testing.T
现在这些信息足以让我们明白,类型为*testing.T
的变量t
是你在测试框架中的 hook(钩子),所以当你想让测试失败时可以执行t.Fail()
之类的操作。
hello.go
package main
import "fmt"
func Hello() string {
return "Hello, world"
}
func main() {
fmt.Println(Hello())
}
hello_test.go
package main
import "testing"
func TestHello(t *testing.T) {
got := Hello()
want := "Hello, world"
if got != want {
t.Errorf("got '%q' want '%q'", got, want)
}
}
此时,运行go test
或者 go test hello_test.go hello.go
(go test -v 则打印完整信息)即可:
// go test
PASS
ok learnGoWithTest/hello 0.005s
// go test -v
=== RUN TestHello
--- PASS: TestHello (0.00s)
PASS
ok learnGoWithTest/hello 0.006s
或者测试失败时如下:
=== RUN TestHello
TestHello: hello_test.go:10: got '"Hello, world"' want '"Hello, world11"'
--- FAIL: TestHello (0.00s)
FAIL
exit status 1
FAIL learnGoWithTest/hello 0.010s
(二)t.Helper()
hello.go
package main
import "fmt"
const englishHelloPrefix = "Hello, "
func Hello(name string) string {
if name == "" {
name = "World"
}
return englishHelloPrefix + name
}
func main() {
fmt.Println(Hello("world"))
}
hello_test.go
package main
import "testing"
func TestHello(t *testing.T) {
assertCorrectMessage := func(t *testing.T, got, want string) {
t.Helper()
if got != want {
t.Errorf("got '%q' want '%q'", got, want)
}
}
t.Run("saying hello to people", func(t *testing.T) {
got := Hello("Chris")
want := "Hello, Chris1"
assertCorrectMessage(t, got, want)
})
t.Run("empty string defaults to 'world'", func(t *testing.T) {
got := Hello("")
want := "Hello, World1"
assertCorrectMessage(t, got, want)
})
}
t.Helper()
需要告诉测试套件这个方法是辅助函数(helper)。通过这样做,当测试失败时所报告的行号将在函数调用中而不是在辅助函数内部。这将帮助其他开发人员更容易地跟踪问题。如果你仍然不理解,请注释掉它,使测试失败并观察测试输出。
输出对比:
注释掉 t.Helper()
时,错误提示的行号为19,是真正抛出错误的代码行号:
=== RUN TestHello
--- FAIL: TestHello (0.00s)
=== RUN TestHello/saying_hello_to_people
TestHello/saying_hello_to_people: hello_test.go:19: got '"Hello, Chris"' want '"Hello, Chris1"'
--- FAIL: TestHello/saying_hello_to_people (0.00s)
=== RUN TestHello/empty_string_defaults_to_'world'
TestHello/empty_string_defaults_to_'world': hello_test.go:19: got '"Hello, World"' want '"Hello, World1"'
--- FAIL: TestHello/empty_string_defaults_to_'world' (0.00s)
FAIL
未注释掉 t.Helper()
时,错误提示的行号为26和32,是调用包含t.Helper()
代码的函数的代码行号:
=== RUN TestHello
--- FAIL: TestHello (0.00s)
=== RUN TestHello/saying_hello_to_people
TestHello/saying_hello_to_people: hello_test.go:26: got '"Hello, Chris"' want '"Hello, Chris1"'
--- FAIL: TestHello/saying_hello_to_people (0.00s)
=== RUN TestHello/empty_string_defaults_to_'world'
TestHello/empty_string_defaults_to_'world': hello_test.go:32: got '"Hello, World"' want '"Hello, World1"'
--- FAIL: TestHello/empty_string_defaults_to_'world' (0.00s)
FAIL
(三)return & switch
func Hello(name string, language string) string {
if name == "" {
name = "World"
}
return greetingPrefix(language) + name
}
func greetingPrefix(language string) (prefix string) {
switch language {
case french:
prefix = frenchHelloPrefix
case spanish:
prefix = spanishHelloPrefix
default:
prefix = englishHelloPrefix
}
return
}
一些新的概念:
- 在我们的函数签名中,我们使用了 命名返回值(prefix string)。
- 这将在你的函数中创建一个名为 prefix 的变量。它将被分配「零」值。这取决于类型,例如 int 是 0,对于字符串它是 ""。你只需调用 return 而不是 return prefix 即可返回所设置的值。
- 这将显示在 Go Doc 中,所以它使你的代码更加清晰。
- 如果没有其他 case 语句匹配,将会执行 default 分支。
- 函数名称以小写字母开头。在 Go 中,公共函数以大写字母开始,私有函数以小写字母开头。我们不希望我们算法的内部结构暴露给外部,所以我们将这个功能私有化。
引用
欢迎大家关注我的公众号
有疑问加站长微信联系(非本文作者)