Go測試開發(一) 怎麼寫Go程式碼

東方er發表於2020-09-05

安裝過程略過,網上搜一大把。

介紹

本文會在一個module中開發一個簡單的Go package。

同時介紹go tool(也就是go命令列)。

以及如何fetch,build和install Go的modules,packages,commands。

程式碼組織

Go是按packages來組織程式碼的。一個package == 一個目錄。

同一個package中的functions,types,variables,和constants是共享的。也就是包訪問許可權,java預設也是包訪問許可權。

packages是放在module中的,module是通過go.mod檔案來定義的。典型的,一個repository只有一個go.mod,放在根目錄。

可以使用go mod init name來建立這個檔案。在go run後會生成go.sum檔案,內容是go.mod的加密雜湊。

repository也允許有多個module,module的packages是go.mod所在的目錄,如果子目錄也有go.mod,那麼這個子目錄的packages就屬於子目錄module。

第一個程式

假設module path是example.com/user/hello

$ mkdir hello # Alternatively, clone it if it already exists in version control.
$ cd hello
$ go mod init example.com/user/hello
go: creating new go.mod: module example.com/user/hello
$ cat go.mod
module example.com/user/hello

go 1.14
$

Go原始檔的第一個語句必須是package name。程式入口必須是package main

package main

import "fmt"

func main() {
	fmt.Println("Hello, world.")
}

喜聞樂見Hello World。

現在可以build和install,

$ go install example.com/user/hello
$

這條命令會build然後生成可執行二進位制檔案(這是我比較喜歡Go的一個原因,直接生成可執行檔案,省去了安裝依賴的麻煩)。

buildinstall命令都可以生成可執行檔案。不同點在於

  • go build 不能生成包檔案, go install 可以生成包檔案
  • go build 生成可執行檔案在當前目錄下, go install 生成可執行檔案在bin目錄下

install生成檔案的bin目錄是根據環境變數來的。按以下順序檢查

  • GOBIN
  • GOPATH

如果都沒有設定,就會生成到預設GOPATH(Linux $HOME/go 或Windows %USERPROFILE%\go)。

示例的二進位制檔案會生成到$HOME/go/bin/hello(Windows的話就是%USERPROFILE%\go\bin\hello.exe)。

可以檢視環境變數GOBIN和GOPATH的值

go env

也可以設定GOBIN

$ go env -w GOBIN=/somewhere/else/bin
$

設定後可以重置

$ go env -u GOBIN
$

GOPATH需要到系統環境變數進行修改。

install等命令需要在原始檔目錄下執行,準確點說是“當前工作目錄”。否則會報錯。

在當前目錄執行,以下等價

$ go install example.com/user/hello
$ go install .
$ go install

驗證下結果,為了方便,新增install目錄到PATH

# Windows users should consult https://github.com/golang/go/wiki/SettingGOPATH
# for setting %PATH%.
$ export PATH=$PATH:$(dirname $(go list -f '{{.Target}}' .))
$ hello
Hello, world.
$

如果cd到了install的bin目錄,也可以直接

$ hello
Hello, world.
$

現階段Go的很多庫都是放在GitHub等程式碼託管網站上面的,使用Git進行提交

$ git init
Initialized empty Git repository in /home/user/hello/.git/
$ git add go.mod hello.go
$ git commit -m "initial commit"
[master (root-commit) 0b4507d] initial commit
 1 file changed, 7 insertion(+)
 create mode 100644 go.mod hello.go
$

Go命令通過請求相應的HTTPS URL,並讀取嵌入在HTML響應中的後設資料<meta>標籤,來定位包含給定module path的repository

Bitbucket (Git, Mercurial)

	import "bitbucket.org/user/project"
	import "bitbucket.org/user/project/sub/directory"

GitHub (Git)

	import "github.com/user/project"
	import "github.com/user/project/sub/directory"

Launchpad (Bazaar)

	import "launchpad.net/project"
	import "launchpad.net/project/series"
	import "launchpad.net/project/series/sub/directory"

	import "launchpad.net/~user/project/branch"
	import "launchpad.net/~user/project/branch/sub/directory"

IBM DevOps Services (Git)

	import "hub.jazz.net/git/user/project"
	import "hub.jazz.net/git/user/project/sub/directory"

很多託管網站已經為Go的repository提供了後設資料,為了共享module,最簡單的辦法就是讓module path匹配repository的URL。

從module import packages

先在名字為morestrings的package中建立一個reverse.go檔案,實現字串反轉

// Package morestrings implements additional functions to manipulate UTF-8
// encoded strings, beyond what is provided in the standard "strings" package.
package morestrings

// ReverseRunes returns its argument string reversed rune-wise left to right.
func ReverseRunes(s string) string {
	r := []rune(s)
	for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r)
}

由於ReverseRunes函式是大寫的,所以是公有的,可以被其他packages import。

先build測試下編譯成功

$ cd $HOME/hello/morestrings
$ go build
$

因為只是在package中,不是在module根目錄,go build不會生成檔案,而是會把compile後的package儲存到local build cache中。

接著在hello.go中import

package main

import (
	"fmt"

	"example.com/user/hello/morestrings"
)

func main() {
	fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
}

然後install hello

$ go install example.com/user/hello

驗證,import成功,字串反轉

$ hello
Hello, Go!

從遠端remore modules import packages

可以用import path通過版本控制系統來獲取package原始碼,如Git或Mercurial。

示例,使用github.com/google/go-cmp/cmp

package main

import (
	"fmt"

	"example.com/user/hello/morestrings"
	"github.com/google/go-cmp/cmp"
)

func main() {
	fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
	fmt.Println(cmp.Diff("Hello World", "Hello Go"))
}

當執行命令go install go build go run的時候,go命令會自動下載遠端module,然後寫到go.mod檔案中

$ go install example.com/user/hello
go: finding module for package github.com/google/go-cmp/cmp
go: downloading github.com/google/go-cmp v0.4.0
go: found github.com/google/go-cmp/cmp in github.com/google/go-cmp v0.4.0
$ hello
Hello, Go!
  string(
- 	"Hello World",
+ 	"Hello Go",
  )
$ cat go.mod
module example.com/user/hello

go 1.14

require github.com/google/go-cmp v0.4.0
$

國內容易超時,可以使用代理走國內映象

七牛雲

go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct

阿里雲

go env -w GO111MODULE=on
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct

module依賴會自動下載到GOPATH指定目錄的pkg/mod子目錄。

module指定版本的下載內容,是在所有其他require這個版本的modules中共享的,所以go命令會標記這些檔案和目錄為只讀的。

可以使用命令刪除所有下載的modules

$ go clean -modcache
$

測試

Go有個輕量的測試框架,go testtesting package

測試框架識別以_test.go結尾的檔案,包含TestXXX命名的函式,函式簽名func (t *testing.T)。如果函式呼叫失敗如t.Errort.Fail,那麼test就會失敗。

示例,新建$HOME/hello/morestrings/reverse_test.go檔案,新增morestrings package的測試程式碼

package morestrings

import "testing"

func TestReverseRunes(t *testing.T) {
	cases := []struct {
		in, want string
	}{
		{"Hello, world", "dlrow ,olleH"},
		{"Hello, 世界", "界世 ,olleH"},
		{"", ""},
	}
	for _, c := range cases {
		got := ReverseRunes(c.in)
		if got != c.want {
			t.Errorf("ReverseRunes(%q) == %q, want %q", c.in, got, c.want)
		}
	}
}

執行測試

$ go test
PASS
ok  	example.com/user/morestrings 0.165s
$

版權申明:本文為博主原創文章,轉載請保留原文連結及作者。
Go測試開發(一) 怎麼寫Go程式碼

相關文章