#使用包和測試管理項目
Go天生就是為了支持良好的項目管理體驗而設計的。
**包**
在軟件工程的實踐中,我們會遇到很多功能重復的代碼,比如去除字符串首尾的空格。高質量軟件產品的特點就是它的部分代碼是可以重用的,比如你不必每次寫個函數去去除字符串首尾的空格。
我們上面講過變量,結構體,接口和函數等,事實上所謂的包,就是把一些用的多的這些變量,結構體,接口和函數等統一放置在一個邏輯塊中。并且給它們起一個名字,這個名字就叫做包名。
例如我們上面用的最多的fmt包,這個包提供了很多格式化輸出的函數,你可以在自己的代碼中引用這個包,來做格式化輸出,而不用你自己每次去寫個函數。一門成熟的語言都會提供齊全的基礎功能包供人調用。
使用包有三個好處
1. 可以減少函數名稱重復,因為不同包中可以存在名稱相同的函數。否則得話,你得給這些函數加上前綴或者后綴以示區別。
2. 包把函數等組織在一起,方便你查找和重用。比如你想用Println()函數輸出一行字符串,你可以很方便地知道它在fmt包中,直接引用過來用就可以了。
3. 使用包可以加速程序編譯。因為包是預編譯好的,你改動自己代碼得時候,不必每次去把包編譯一下。
**創建包**
我們現在來舉個例子,用來演示Go的項目管理。
首先我們在目錄`/Users/jemy/JemyGraw/GoLang`下面創建文件夾`pkg_demo`。然后在`pkg_demo`里面創建`src`文件夾
。然后再在`main`文件夾里面創建`main.go`文件。另外為了演示包的創建,我們在`src`目錄下面創建文件夾`net.duokr`,然后再在`net.duokr`文件夾里面創建`math`文件夾,這個文件夾名稱就是這個文件夾下面go文件的包名稱。然后我們再創建一個`math_pkg.go`文件,之所以取這個名字而不是`math.go`只是為了說明這個文件名稱和包名不需要一致。然后我們還創建了一個`math_pkg_test.go`文件作為包的測試用例文件。整體結構如下:
.
└── src
├── main
│?? ├── build.sh
│?? └── main.go
└── net.duokr
└── math
├── math_pkg.go
└── math_pkg_test.go
其中build.sh是我們為了編譯這個項目而寫的腳本,因為編譯項目需要幾條命令,把它寫在腳本文件中方便使用。另外為了能夠讓build.sh能夠執行,使用`chmod +x build.sh`為它賦予可執行權限。build.bat是Windows下面的編譯腳本。
我們來看一下`math_pkg.go`的定義:
package math
func Add(a, b int) int {
return a + b
}
func Subtract(a, b int) int {
return a - b
}
func Multiply(a, b int) int {
return a * b
}
func Divide(a, b int) int {
if b == 0 {
panic("Can not divided by zero")
}
return a / b
}
首先是包名,然后是幾個函數定義,這里我們會發現這些`函數定義首字母都是大寫`,`Go規定了只有首字母大寫的函數才能從包導出使用,即其他調用這個包中函數的代碼只能調用那些導出的函數`。
我們再看一下`main.go`的定義:
package main
import (
"fmt"
math "net.duokr/math"
)
func main() {
var a = 100
var b = 200
fmt.Println("Add demo:", math.Add(a, b))
fmt.Println("Substract demo:", math.Subtract(a, b))
fmt.Println("Multiply demo:", math.Multiply(a, b))
fmt.Println("Divide demo:", math.Divide(a, b))
}
在main.go里面,我們使用import關鍵字引用我們自定義的包math,引用的方法是從main包平行的文件夾net.duokr開始,后面跟上包名math。這里面我們給這個長長的包名起了一個別名就叫math。然后分別調用math包里面的函數。
最后我們看一下我們的編譯腳本:
export GOPATH=$GOPATH:/Users/jemy/JemyGraw/GoLang/pkg_demo
export GOBIN=/Users/jemy/JemyGraw/GoLang/pkg_demo/bin
go build net.duokr/math
go build main.go
go install main
第一行,我們將項目路徑加入GOPATH中,這樣待會兒編譯main.go的時候才能找到我們自定義的包;
第二行,我們設置本項目的安裝目錄,第五行的命令將編譯好的文件放到這個目錄下面;
第三行,我們編譯我們的自定義包;
第四行,我們編譯我們main.go文件;
第五行,將編譯好的文件安裝到指定目錄下。
這里還有一個Windows下面的編譯腳本build.bat:
@echo off
set GOPATH=GOPATH;C:\JemyGraw\GoLang\pkg_demo
set GOBIN=C:\JemyGraw\GoLang\pkg_demo\bin
go build net.duokr\math
go build main.go
go install main
好了,運行腳本編譯一下,在main文件夾和bin文件夾下面都會生成一個可執行文件。
這個時候文件夾結構為:
.
├── bin
│?? └── main
├── pkg
│?? └── darwin_386
│?? └── net.duokr
│?? └── math.a
└── src
├── main
│?? ├── build.bat
│?? ├── build.sh
│?? ├── main
│?? └── main.go
└── net.duokr
└── math
├── math_pkg.go
└── math_pkg_test.go
運行一下,輸出結果為:
Add demo: 300
Substract demo: -100
Multiply demo: 20000
Divide demo: 0
好了,包的使用介紹完畢,我們再來看一下測試用例怎么寫。
**測試**
在上面的例子中,我們發現我們自定義的包下面還有一個math_pkg_test.go文件,這個文件包含了本包的一些測試用例。而且Go會把以`_test.go`結尾的文件當作是測試文件。
測試怎么寫,當然是用assert來判斷程序的運行結果是否和預期的相同了。
我們來看看這個math包的測試用例。
package math
import (
"testing"
)
func TestAdd(t *testing.T) {
var a = 100
var b = 200
var val = Add(a, b)
if val != a+b {
t.Error("Test Case [", "TestAdd", "] Failed!")
}
}
func TestSubtract(t *testing.T) {
var a = 100
var b = 200
var val = Subtract(a, b)
if val != a-b {
t.Error("Test Case [", "TestSubtract", "] Failed!")
}
}
func TestMultiply(t *testing.T) {
var a = 100
var b = 200
var val = Multiply(a, b)
if val != a*b {
t.Error("Test Case [", "TestMultiply", "] Failed!")
}
}
func TestDivideNormal(t *testing.T) {
var a = 100
var b = 200
var val = Divide(a, b)
if val != a/b {
t.Error("Test Case [", "TestDivideNormal", "] Failed!")
}
}
將路徑切換到測試文件所在目錄,運行`go test`命令,go會自動測試所有的測試用例。
在上面的例子中,測試用例的特點是以函數名以`Test`開始,而且具有唯一參數`t *testing.T`。
**小結**
Go提供的包和用例測試是構建優秀的軟件產品的基礎,只要我們不斷學習,努力去做,一定可以做的很好。