Contents
1.8. 编译与工具¶
1.8.1. 1. 需要GOPATH支持的方式¶
Go的工作区目录结构有bin、pkg、src三个(在GOPATH目录下):
bin:编译后的可执行程序的存储目录。
pkg:编译时生成的对象文件。
src:库文件。
这些都是Go设计者的约定,只需按照这样的方式组织目录结构即可。GOPATH环境变量生效时个人项目可以在src目录下创建新目录,第三方库存放在src的github.com目录下。
1.8.2. 2.不需要GOPATH环境变量支持的设置¶
不需要GOPATH环境变量支持的设置的示例如下:
export GOROOT=/usr/local/go
export GOPATH=/usr/share/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
对于这种方式,开发者可以在任意目录下创建子目录作为项目程序的目录,使用的第三方库会下载至$GOPATH/pkg/mod目录下。
比如:
$ mkdir goproject
$ cd goproject
# 此时只需要在go-anything目录下执行go mod init命令即可。
$ go mod init
自动创建go.mod、go.sum作为版本管理的文件。
要使用第三方库,只需执行go get命令,它会自动更改go.mod、go.sum文件,推荐使用这种方式作为项目程序的版本管理方式。
安装Go语言开发系统后,内置的命令行工具常用的命令如下:
go build:将程序编译成可执行文件。
go run:将程序先编译成可执行文件,再运行程序。
go fmt:格式化代码,比如换行、缩进等。
go test:运行测试的命令。
go get:下载第三方库的常用命令。
go version:查看当前操作系统中安装的Go语言系统的版本信息。
go env:查看当前操作系统和Go语言相关的环境变量的值。
2.1 在项目中使用go module¶
既有项目
如果需要对一个已经存在的项目启用go module,可以按照以下步骤操作:
1. 在项目目录下执行go mod init,生成一个go.mod文件。
2. 执行go get,查找并记录当前项目的依赖,同时生成一个go.sum记录每个依赖库的版本和哈希值。
新项目
对于一个新创建的项目,我们可以在项目文件夹下按照以下步骤操作:
1. 执行go mod init 项目名命令,在当前项目文件夹下创建一个go.mod文件。
2. 手动编辑go.mod中的require依赖项或执行go get自动发现、维护依赖。
2.2 go module版本管理工具使用¶
1.8.3. 3.编译(go build)¶
Go语言的编译速度非常快。Go1.9版本后默认利用Go语言的并发特性进行函数粒度的并发编译。
Go语言的程序编写基本以源码方式,无论是直接的代码还是第三方代码, 并且以GOPATH作为母子目录和一套完整的工程目录规则。
因此Go语言中日常编译时无需像C++一样配置各种包含路径、链接库地址等。
3.1 go build无参数编译¶
将源码编译成可执行文件,go build有很多种编译方法,如: 无参数编译、文件列表编译、指定包编译,使用这些方法都可以输出可执行文件。
$ tree
.
└── gobuild
├── lib.go
└── main.go
lib.go代码如下
package main
import "fmt"
func pkgFunc() {
fmt.Println("call pkgFunc")
}
main.go代码如下
package main
import "fmt"
func main() {
// 同包的函数
pkgFunc()
fmt.Println("hello world")
}
D:\go_studay\go_path\src\awesomeProject20>cd gobuild
D:\go_studay\go_path\src\awesomeProject20\gobuild>go build
$ ls
gobuild.exe lib.go main.go
D:\go_studay\go_path\src\awesomeProject20\gobuild>./gobuild.exe
call pkgFunc
hello world
3.2 go build+文件列表¶
编译同目录的多个源码文件时,可以在go build的后面提供多个文件名,go build会编译这些源码。 输出可执行文件, go build + 文件列表的格式如下:
$ ls
gobuild.exe lib.go main.go
18793@DESKTOP-PMJTNGI /cygdrive/d/go_studay/go_path/src/awesomeProject20/gobuild
$ go build main.go lib.go
18793@DESKTOP-PMJTNGI /cygdrive/d/go_studay/go_path/src/awesomeProject20/gobuild
$ ls
gobuild.exe lib.go main.exe main.go
$ ./main.exe
call pkgFunc
hello world
提示:使用“go build +文件列表”方式编译时,可执行文件默认选择文件列表中第一个源码文件作为可执行文件名输出。
如果需要指定输出可执行文件名,可以使用-o参数。
$ go build -o myexe main.go lib.go
18793@DESKTOP-PMJTNGI /cygdrive/d/go_studay/go_path/src/awesomeProject20/gobuild
$ ./myexe
call pkgFunc
hello world
在go build和文件列表之间插入了-o myexe参数,表示指定输出文件名为myexe。
注意:
“go build + 文件列表” 编译方式编译时,文件列表中的每个文件必须是同一个包的Go源码,也就是说不能像C++那样,将所有工程的Go源码使用文件列表方式进行编译,编译复杂工程时需要用“指定包编译”的方式。
“go build+文件列表” 方式更适合使用Go语言编写只有少量文件的工具。
3.3 go build+包¶
go build 和clean 命令会执行编译和清理的工作
“go build+包”在设置GOPATH后,可以直接根据包名进行编译,即使包内文件被增删也不影响编译指令。
$ tree gobuild02/
gobuild02/
├── main.go
└── mypkg
└── mypkg.go
main.go代码如下
package main
import (
"awesomeProject20/gobuild02/mypkg"
"fmt"
)
func main() {
mypkg.CustomPkgFunc()
fmt.Println("hello world")
}
mypkg.go代码如下
package mypkg
import "fmt"
func CustomPkgFunc() {
fmt.Println("call CustomPkgFunc")
}
设置的GOPATH 路径为D:\go_studay\go_path\
按包编译命令
$ go build -o main awesomeProject20/gobuild02
-o 执行指定输出文件为main,后面接要编译的包名,包名是相对于GOPATH下的src目录开始的。
18793@DESKTOP-PMJTNGI /cygdrive/d/go_studay/go_path/src/awesomeProject20
$ ll
总用量 2060
drwxrwx---+ 1 18793 18793 0 1月 15 11:14 gobuild
drwxrwx---+ 1 18793 18793 0 1月 15 11:18 gobuild02
-rwxrwx---+ 1 18793 18793 2107392 1月 15 11:21 main
18793@DESKTOP-PMJTNGI /cygdrive/d/go_studay/go_path/src/awesomeProject20
$ ./main
call CustomPkgFunc
hello world
也可以在指定包的时候使用通配符。3个点表示匹配所有的字符串。例如,下面的命令会编译chapter3
目录下的所有包:
$ go build github.com/goinaction/code/chapter3/...
除了指定包,大部分Go命令使用短路径作为参数。例如,下面两条命令的效果相同:
$ go build wordcount.go
$ go build .
3.4 go build编译时的附加参数¶
1.8.4. 4.编译后运行(go run)¶
Python和Lua语言可以在不输出二进制的情况下,将代码使用虚拟机直接执行, Go语言虽然不使用虚拟机,但可使用go run指令达到同样的效果。
go run命令会编译源码,兵器直接执行源码的main()函数,不会在当前目录下留下可执行文件。
$ tree gorun/
gorun/
└── main.go
我们准备一个main.go文件
package main
import (
"fmt"
"os"
)
func main() {
fmt.Println("args:",os.Args)
}
$ cd gorun/
$ go run main.go --file xxx.go
args: [D:\Cygwin\tmp\go-build804207966\b001\exe\main.exe --file xxx.go]
go run不会在运行目录下生成任何文件,可执行文件被放在临时文件中被执行,工作目录被设置为当前目录。
在go run的后部可以添加参数,这部分参数会作为代码可以接受的命令行输入提供给程序。
go run不能使用“go run+包”的方式进行编译,如需快速编译运行包,需要使用如下步骤来代替:
(1)使用go build生成可执行文件
(2)运行可执行文件
1.8.5. 5.编译并安装(go install)¶
go install的功能和go build类似,附加参数绝大多数都可以与go build通用。
go install只是将编译的中间文件放在GOPATH的pkg目录下,以及固定地将编译结果放在GOPATH的bin目录下。
使用go install来执行代码11-2,参考下面的shell:
$ export GOPATH=/home/davy/golangbook/code
$ go install chapter11/goinstall
编译完成后的目录结构如下:
.
├── bin
│ └── goinstall
├── pkg
│ └── linux_amd64
│ └── chapter11
│ └── goinstall
│ └── mypkg.a
└── src
└── chapter11
├── gobuild
│ ├── lib.go
│ └── main.go
└── goinstall
├── main.go
└── mypkg
└── mypkg.go
go install是建立在GOPATH上的,无法在独立的目录里使用go install
GOPATH下的bin目录放置的是使用go install生成的可执行文件,可执行文件的名称来自于编译时的包
go install输出目录始终为GOPATH下的bin目录,无法使用-o附加参数进行自定义
GOPATH下的pkg目录放置的是编译期间的中间文件。
1.8.6. 6.一键获取代码、编译并安装(go get)¶
go get可以借助代码管理工具通远程拉取或更新代码包及其依赖包,并自动完成编译和安装。 整个过程就像安装一个App一样简单。
使用go get前,需要安装与远程包匹配的代码管理工具,如Git、SVN、HG等,参数中需要提供一个包名。
6.1 远程包的路径格式¶
6.2 go get + 远程包¶
默认情况下,go get可以直接使用。例如,想获取go的源码并编译,使用下面命令行即可: 使用go get前,需要安装与远程包匹配的代码管理工具,如Git、SVN、HG等,参数中需要提供一个包名。
go get github.com/davyxu/cellnet
获取前,请确保GOPATH已经设置。Go 1.8版本之后,GOPATH默认在用户目录的go文件夹下。
cellnet只是一个网络库,并没有可执行文件,因此在go get操作成功后GOPATH下的bin目录下不会有任何编译好的二进制文件。
需要测试获取并编译二进制的,可以尝试下面的这个命令。当获取完成后,就会自动在GOPATH的bin目录下生成编译好的二进制文件。
$ go get github.com/davyxu/tabtoy
如果路径包含URL,可以使用Go工具链从DVCS获取包,并把包的源代码保存在GOPATH
指向的路径里与URL匹配的目录里。 这个获取过程使用go get
命令完成。go get
将获取任意指定的URL的包,或者一个已经导入的包所依赖的其他包。
由于go get
的这种递归特性,这个命令会扫描某个包的源码树,获取能找到的所有依赖包。
//例如:
import "github.com/spf13/viper"
6.3 go get 使用时的附加参数¶
1.8.7. 7. go vet¶
这个命令不会帮开发人员写代码,但如果开发人员已经写了一些代码,vet
命令会帮开发人员检测代码的常见错误。
每次对代码先执行go vet 再将其签入源代码库是一个很好的习惯。
1.8.8. 8.测试(go test)¶
8.1 单元测试-测试和验证代码的框架¶
要开始一个单元测试,需要准备一个go源码文件,在命名文件时需要让文件必须以_test结尾。
单元测试源码文件可以由多个测试用例组成,每个测试用例函数需要以Test为前缀,例如:
func TestXXX(t *testing.T)
测试用例文件不会参与正常源码编译,不会被包含到可执行文件中。
测试用例文件使用go test指令来执行,没有也不需要main()作为函数入口,所有在以_test结尾的源码内 以Test开头的函数会自动被执行。
测试用例可以不传入*testing.T参数。
helloworld_test.go
package goTest
import "testing"
func TestHelloWorld(t *testing.T) {
t.Log("hello world")
}
执行如下:
GOROOT=C:\Go #gosetup
GOPATH=D:\go_studay\go_path #gosetup
C:\Go\bin\go.exe test -c -o C:\Users\18793\AppData\Local\Temp\___TestHelloWorld_in_awesomeProject20_goTest.exe awesomeProject20/goTest #gosetup
C:\Go\bin\go.exe tool test2json -t C:\Users\18793\AppData\Local\Temp\___TestHelloWorld_in_awesomeProject20_goTest.exe -test.v -test.run ^TestHelloWorld$ #gosetup
=== RUN TestHelloWorld
--- PASS: TestHelloWorld (0.00s)
helloworld_test.go:6: hello world
PASS
8.2 运行指定单元测试用例¶
go test指定文件时默认执行文件内的所有测试用例,可以使用-run参数选择需要的测试用例单独执行。 参考如下代码:
select_test.go
package goTest
import "testing"
func TestA(t *testing.T) {
t.Log("A")
}
func TestAK(t *testing.T) {
t.Log("AK")
}
func TestB(t *testing.T) {
t.Log("B")
}
func TestC(t *testing.T) {
t.Log("C")
}
go test -run TestA select_test.go
ok command-line-arguments 0.257s
go test -run TestB helloworld_test.go
ok command-line-arguments 0.250s [no tests to run]
TestA和TestAK都被执行,原因是-run 跟随的测试用例的名称支持正则表达式,使用-run TestA$即可执行TestA的测试用例。
8.3 标记单元测试结果¶
当需要终止当前测试用例时,可以使用FailNow,参考下面代码:
package goTest
import "testing"
func TestFailNow(t *testing.T) {
t.FailNow()
}
package goTest
import (
"fmt"
"testing"
)
func TestFailNow(t *testing.T) {
fmt.Println("before fail")
t.Fail()
fmt.Println("after fail")
}
测试结果如下:
GOROOT=C:\Go #gosetup
GOPATH=D:\go_studay\go_path #gosetup
C:\Go\bin\go.exe test -c -o C:\Users\18793\AppData\Local\Temp\___TestFailNow_in_awesomeProject20_goTest.exe awesomeProject20/goTest #gosetup
C:\Go\bin\go.exe tool test2json -t C:\Users\18793\AppData\Local\Temp\___TestFailNow_in_awesomeProject20_goTest.exe -test.v -test.run ^TestFailNow$ #gosetup
=== RUN TestFailNow
before fail
after fail
--- FAIL: TestFailNow (0.00s)
FAIL
8.4 单元测试日志¶
每个测试用例可能并发执行,使用yesying.T提供的日志输出可以保证日志跟随这个测试上下文一起打印输出。
testing.T提供了几种日志输出方法。 
8.5 测试例子¶
1. 测试函数¶
每个测试函数必须导入 testing 包. 测试函数有如下的签名:
func TestName(t *testing.T) {
// ...
}
测试函数的名字必须以Test开头, 可选的后缀名必须以大写字母开头:
func TestSin(t *testing.T) { /* ... */ }
func TestCos(t *testing.T) { /* ... */ }
func TestLog(t *testing.T) { /* ... */ }
word1/word1.go
只有一个函数 IsPalindrome 用于检查一个字符串是否从前向后和从后向前读都一样.
package word
// IsPalindrome reports whether s reads the same forward and backward.
// (Our first attempt.)
func IsPalindrome(s string) bool {
for i := range s {
if s[i] != s[len(s)-1-i] {
return false
}
}
return true
}
在相同的目录下, word_test.go 文件包含了 TestPalindrome 和 TestNonPalindrome 两个测试函数. 每一个都是测试 IsPalindrome 是否给出正确的结果, 并使用 t.Error 报告失败:
package word
import "testing"
func TestPalindrome(t *testing.T) {
if !IsPalindrome("detartrated") {
t.Error(`IsPalindrome("detartrated") = false`)
}
if !IsPalindrome("kayak") {
t.Error(`IsPalindrome("kayak") = false`)
}
}
func TestNonPalindrome(t *testing.T) {
if IsPalindrome("palindrome") {
t.Error(`IsPalindrome("palindrome") = true`)
}
}
go test (或 go build) 命令
如果没有参数指定包那么将默认采用当前目录对应的包.
我们可以用下面的命令构建和运行测试.
$ cd $GOPATH/src/gopl.io/ch11/word1
$ go test
ok gopl.io/ch11/word1 0.008s
还比较满意, 我们运行了这个程序, 不过没有提前退出是因为还没有遇到BUG报告. 一个法国名为 Noelle Eve Elleon 的用户抱怨 IsPalindrome 函数不能识别 ‘‘été.’’. 另外一个来自美国中部用户的抱怨是不能识别 ‘‘A man, a plan, a canal: Panama.’’. 执行特殊和小的BUG报告为我们提供了新的更自然的测试用例.
func TestFrenchPalindrome(t *testing.T) {
if !IsPalindrome("été") {
t.Error(`IsPalindrome("été") = false`)
}
}
func TestCanalPalindrome(t *testing.T) {
input := "A man, a plan, a canal: Panama"
if !IsPalindrome(input) {
t.Errorf(`IsPalindrome(%q) = false`, input)
}
}
为了避免两次输入较长的字符串, 我们使用了提供了有类似 Printf 格式化功能的 Errorf 函数来汇报错误结果.
当添加了这两个测试用例之后, go test 返回了测试失败的信息.
$ go test
--- FAIL: TestFrenchPalindrome (0.00s)
word_test.go:23: IsPalindrome("été") = false
--- FAIL: TestCanalPalindrome (0.00s)
word_test.go:30: IsPalindrome("A man, a plan, a canal: Panama") = false
FAIL
exit status 1
FAIL go-language-Bible/ch11/word1 0.460s
参数 -v 用于打印每个测试函数的名字和运行时间:
$ go test -v
=== RUN TestPalindrome
--- PASS: TestPalindrome (0.00s)
=== RUN TestNonPalindrome
--- PASS: TestNonPalindrome (0.00s)
=== RUN TestFrenchPalindrome
word_test.go:23: IsPalindrome("été") = false
--- FAIL: TestFrenchPalindrome (0.00s)
=== RUN TestCanalPalindrome
word_test.go:30: IsPalindrome("A man, a plan, a canal: Panama") = false
--- FAIL: TestCanalPalindrome (0.00s)
FAIL
exit status 1
FAIL go-language-Bible/ch11/word1 0.460s
参数 -run 是一个正则表达式,
只有测试函数名被它正确匹配的测试函数才会被 go test 运行:
$ go test -v -run="French|Canal"
当然, 一旦我们已经修复了失败的测试用例, 在我们提交代码更新之前,
我们应该以不带参数的 go test 命令运行全部的测试用例,
以确保更新没有引入新的问题.
我们现在的任务就是修复这些错误. 简要分析后发现第一个BUG的原因是我们采用了 byte 而不是 rune 序列, 所以像 “été” 中的 é 等非 ASCII 字符不能正确处理. 第二个BUG是因为没有忽略空格和字母的大小写导致的.
针对上述两个BUG, 我们仔细重写了函数:
package word
import "unicode"
// IsPalindrome reports whether s reads the same forward and backward.
// (Our first attempt.)
func IsPalindrome(s string) bool {
var letters []rune
for _, r := range s {
if unicode.IsLetter(r) {
letters = append(letters, unicode.ToLower(r))
}
}
for i := range letters {
if letters[i] != letters[len(letters)-1-i] {
return false
}
}
return true
}
同时我们也将之前的所有测试数据合并到了一个测试中的表格中.
package word
import "testing"
func TestIsPalindrome(t *testing.T) {
var tests = []struct {
input string
want bool
}{
{"", true},
{"a", true},
{"aa", true},
{"ab", false},
{"kayak", true},
{"detartrated", true},
{"A man, a plan, a canal: Panama", true},
{"Evil I did dwell; lewd did I live.", true},
{"Able was I ere I saw Elba", true},
{"été", true},
{"Et se resservir, ivresse reste.", true},
{"palindrome", false}, // non-palindrome
{"desserts", false}, // semi-palindrome
}
for _, test := range tests {
if got := IsPalindrome(test.input); got != test.want {
t.Errorf("IsPalindrome(%q) = %v", test.input, got)
}
}
}
我们的新测试阿都通过了:
$ go test gopl.io/ch11/word2
ok gopl.io/ch11/word2 0.015s
这种表格驱动的测试在Go中很常见的. 我们很容易想表格添加新的测试数据, 并且后面的测试逻辑也没有冗余, 这样我们可以更好地完善错误信息。
2. 随机测试¶
表格驱动的测试便于构造基于精心挑选的测试数据的测试用例. 另一种测试思路是随机测试, 也就是通过构造更广泛的随机输入来测试探索函数的行为.
import "math/rand"
// randomPalindrome returns a palindrome whose length and contents
// are derived from the pseudo-random number generator rng.
func randomPalindrome(rng *rand.Rand) string {
n := rng.Intn(25) // random length up to 24
runes := make([]rune, n)
for i := 0; i < (n+1)/2; i++ {
r := rune(rng.Intn(0x1000)) // random rune up to '\u0999'
runes[i] = r
runes[n-1-i] = r
}
return string(runes)
}
func TestRandomPalindromes(t *testing.T) {
// Initialize a pseudo-random number generator.
seed := time.Now().UTC().UnixNano()
t.Logf("Random seed: %d", seed)
rng := rand.New(rand.NewSource(seed))
for i := 0; i < 1000; i++ {
p := randomPalindrome(rng)
if !IsPalindrome(p) {
t.Errorf("IsPalindrome(%q) = false", p)
}
}
}
3. 测试一个命令¶
echo 程序编写一个测试. 我们先将程序拆分为两个函数: echo 函数完成真正的工作, main 函数用于处理命令行输入参数和echo可能返回的错误.
echo.go
package main
import (
"flag"
"fmt"
"io"
"os"
"strings"
)
var (
n = flag.Bool("n", false, "omit trailing newline")
s = flag.String("s", " ", "separator")
)
var out io.Writer = os.Stdout // modified during testing
func main() {
flag.Parse()
if err := echo(!*n, *s, flag.Args()); err != nil {
fmt.Fprintf(os.Stderr, "echo: %v\n", err)
os.Exit(1)
}
}
func echo(newline bool, sep string, args []string) error {
fmt.Fprint(out, strings.Join(args, sep))
if newline {
fmt.Fprintln(out)
}
return nil
}
命令行执行如上代码
$ go run echo.go -n=true -s="," a b c
a,b,c
$ go run echo.go -n=true -s="|" a b c
a|b|c
在测试中吗我们可以用各种参数和标标志调用 echo 函数, 然后检测它的输出是否正确, 我们通过增加参数来减少 echo 函数对全局变量的依赖. 我们还增加了一个全局名为 out 的变量来替代直接使用 os.Stdout, 这样测试代码可以根据需要将 out 修改为不同的对象以便于检查. 下面就是 echo_test.go 文件中的测试代码:
echo_test.go
package main
import (
"bytes"
"fmt"
"testing"
)
func TestEcho(t *testing.T) {
var tests = []struct {
newline bool
sep string
args []string
want string
}{
{true, "", []string{}, "\n"},
{false, "", []string{}, ""},
{true, "\t", []string{"one", "two", "three"}, "one\ttwo\tthree\n"},
{true, ",", []string{"a", "b", "c"}, "a,b,c\n"},
{false, ":", []string{"1", "2", "3"}, "1:2:3"},
// {true, ",", []string{"a", "b", "c"}, "a b c\n"}, // NOTE: wrong expectation!
}
for _, test := range tests {
descr := fmt.Sprintf("echo(%v, %q, %q)",
test.newline, test.sep, test.args)
out = new(bytes.Buffer) // captured output
if err := echo(test.newline, test.sep, test.args); err != nil {
t.Errorf("%s failed: %v", descr, err)
continue
}
got := out.(*bytes.Buffer).String()
if got != test.want {
t.Errorf("%s = %q, want %q", descr, got, test.want)
}
}
}
错误信息描述了尝试的操作(使用Go类似语法), 实际的行为, 和期望的行为. 通过这样的错误信息, 你可以在检视代码之前就很容易定位错误的原因.
要注意的是在测试代码中并没有调用 log.Fatal 或 os.Exit, 因为调用这类函数会导致程序提前退出;
调用这些函数的特权应该放在 main 函数中. 如果真的有意外的事情导致函数发送 panic, 测试驱动应该尝试 recover, 然后将当前测试当作失败处理. 如果是可预期的错误, 例如非法的用户输入, 找不到文件, 或配置文件不当等应该通过返回一个非空的 error 的方式处理. 幸运的是(上面的意外只是一个插曲), 我们的 echo 示例是比较简单的也没有需要返回非空error的情况.
4. 编写有效的测试¶
assertEqual/biaozhun.go
package assertEqual
import (
"fmt"
)
// A poor assertion function.
func assertEqual(x, y int) {
if x != y {
panic(fmt.Sprintf("%d != %d", x, y))
}
}
assertEqual/biaozhun_test.go
package assertEqual
import (
"strings"
"testing"
)
func TestSplit(t *testing.T) {
s, sep := "a:b:c", ":"
words := strings.Split(s, sep)
if got, want := len(words), 3; got != want {
t.Errorf("Split(%q, %q) returned %d words, want %d",
s, sep, got, want)
}
}
现在的测试不仅报告了调用的具体函数, 它的输入, 和结果的意义; 并且打印的真实返回的值和期望返回的值; 并且即使断言失败依然会继续尝试运行更多的测试.
一旦我们写了这样结构的测试, 下一步自然不是用更多的if语句来扩展测试用例, 我们可以用像 IsPalindrome 的表驱动测试那样来准备更多的 s, sep 测试用例.
1.8.9. 9. 命令¶
假如你已安装了golang环境,你可以在命令行执行go命令查看相关的Go语言命令:
$ go
Go is a tool for managing Go source code.
Usage:
go <command> [arguments]
The commands are:
bug start a bug report
build compile packages and dependencies
clean remove object files and cached files
doc show documentation for package or symbol
env print Go environment information
fix update packages to use new APIs
fmt gofmt (reformat) package sources
generate generate Go files by processing source
get add dependencies to current module and install them
install compile and install packages and dependencies
list list packages or modules
mod module maintenance
run compile and run Go program
test test packages
tool run specified go tool
version print Go version
vet report likely mistakes in packages
Use "go help <command>" for more information about a command.
Additional help topics:
buildconstraint build constraints
buildmode build modes
c calling between Go and C
cache build and test caching
environment environment variables
filetype file types
go.mod the go.mod file
gopath GOPATH environment variable
gopath-get legacy GOPATH go get
goproxy module proxy protocol
importpath import path syntax
modules modules, module versions, and more
module-get module-aware go get
module-auth module authentication using go.sum
packages package lists and patterns
private configuration for downloading non-public code
testflag testing flags
testfunc testing functions
vcs controlling version control with GOVCS
Use "go help <topic>" for more information about that topic.
go env用于打印Go语言的环境信息。
go run命令可以编译并运行命令源码文件。
go get可以根据要求和实际情况从互联网上下载或更新指定的代码包及其依赖包,并对它们进行编译和安装。
go build命令用于编译我们指定的源码文件或代码包以及它们的依赖包。
go install用于编译并安装指定的代码包及它们的依赖包。
go clean命令会删除掉执行其它命令时产生的一些文件和目录。
go doc命令可以打印附于Go语言程序实体上的文档。我们可以通过把程序实体的标识符作为该命令的参数来达到查看其文档的目的。
go test命令用于对Go语言编写的程序进行测试。
go list命令的作用是列出指定的代码包的信息。
go fix会把指定代码包的所有Go语言源码文件中的旧版本代码修正为新版本的代码。
go vet是一个用于检查Go语言源码中静态错误的简单工具。
go tool pprof命令来交互式的访问概要文件的内容。
1.8.10. 10. gb工程¶
示例:
/home/bill/devel/myproject ($PROJECT)
|-- src
| |-- cmd
| | |-- myproject
| | | |-- main.go
| |-- examples
| |-- model
| |-- README.md
|-- vendor
|-- src
|-- bitbucket.org
| |-- ww
| |-- goautoneg
| |-- Makefile
| |-- README.txt
| |-- autoneg.go
| |-- autoneg_test.go
|-- github.com
|-- beorn7
|-- perks
|-- README.md
|-- quantile
|-- bench_test.go
|-- example_test.go
|-- exampledata.txt
|-- stream.go
工程中存放开发人员写的代码的位置
$PROJECT/src/
存放第三方代码的位置
$PROJECT/vendor/src/
gb 一个最好的特点是,不需要重写``导入`` 路径。
可以看看这个工程里的main.go文件的import
语句——没有任何需要为导入第三方库而做的修改,如代码所示。
代码示例 gb工程的导入路径
package main
import (
"bitbucket.org/ww/goautoneg"
"github.com/beorn7/perks"
)
gb工具首先会在``$PROJECT/src/`` 目录中查找代码,如果找不到,会在``$PROJECT/vender/src/`` 目录里查找。与工程相关的整个源代码都会在同一个代码库里。自己写的代码在工程目录的``src/`` 目录中,第三方依赖代码在工程目录的``vender/src`` 子目录中。
这样,不需要配合重写导入路径也可以完成整个构建过程,同时可以把整个工程放到磁盘的任意位置。这些特点,让gb成为社区里解决可重复构建的流行工具。
1.8.11. 11. GO国内无法go get的解决办法¶
Gitee GoProxy仓使用帮助
代理上游
https://goproxy.cn/ https://goproxy.io/
使用方法
#Go 1.13 及以上
go env -w GO111MODULE=on
go env -w GOPROXY=http://mirrors.gitee.com/repository/go-public/
go env -w GOSUMDB=off
#or
export GOPROXY=http://mirrors.gitee.com/repository/go-public/
测试
#test
go get github.com/valyala/fasthttp