├── main.go ├── function ├── calculator.go └── calculator_test.go ├── coverage.txt ├── codecov.yml ├── .travis.yml └── README.md /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kevingo/GoTestExample/function" 6 | ) 7 | 8 | func main() { 9 | fmt.Println(function.Division(6, 2)) 10 | } -------------------------------------------------------------------------------- /function/calculator.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import "errors" 4 | 5 | func Division(a, b float64) (float64, error) { 6 | if b == 0 { 7 | return 0, errors.New("除數不可為零") 8 | } 9 | 10 | return a / b, nil 11 | } 12 | -------------------------------------------------------------------------------- /coverage.txt: -------------------------------------------------------------------------------- 1 | mode: atomic 2 | github.com/kevingo/GoTestExample/function/calculator.go:5.46,6.12 1 2 3 | github.com/kevingo/GoTestExample/function/calculator.go:10.2,10.19 1 1 4 | github.com/kevingo/GoTestExample/function/calculator.go:6.12,8.3 1 1 5 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | precision: 2 3 | round: down 4 | range: "70...100" 5 | 6 | status: 7 | project: 8 | default: 9 | target: auto 10 | if_no_uploads: error 11 | 12 | patch: 13 | default: 14 | if_no_uploads: error 15 | 16 | changes: true 17 | 18 | 19 | comment: 20 | layout: "header, diff, changes, suggestions" 21 | behavior: default -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | sudo: false 4 | 5 | go: 6 | - 1.6 7 | - 1.7 8 | - tip 9 | 10 | before_script: 11 | - go fmt ./... 12 | - go vet $(go list ./...) 13 | 14 | script: 15 | - go test -coverprofile=coverage.txt -covermode=atomic ./function/ 16 | - go test ./function/ -bench=. -cpu=1,2,4,8 17 | 18 | after_success: 19 | - bash <(curl -s https://codecov.io/bash) 20 | 21 | notifications: 22 | email: 23 | - kevingo75@gmail.com 24 | -------------------------------------------------------------------------------- /function/calculator_test.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import ( 4 | "testing" 5 | "fmt" 6 | ) 7 | 8 | func Test_Division(t *testing.T) { 9 | if i, e := Division(6, 2); i != 3 || e != nil { 10 | t.Error("Fail to pass") 11 | } else { 12 | t.Log("Pass") 13 | } 14 | } 15 | 16 | func Test_Division_Zero(t *testing.T) { 17 | if _, e := Division(6, 0); e == nil { 18 | t.Error("Should not go here.") 19 | } else { 20 | t.Log("Check zero correctlly.") 21 | } 22 | } 23 | 24 | func BenchmarkDivision(b *testing.B) { 25 | for i:=0 ; i 33 | } 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GoTestExample [![Build Status](https://api.travis-ci.org/kevingo/GoTestExample.png?branch=master)](https://travis-ci.org/kevingo/GoTestExample) [![codecov.io](http://codecov.io/github/kevingo/GoTestExample/coverage.svg?branch=master)](http://codecov.io/github/kevingo/GoTestExample?branch=master) 2 | 3 | 4 | 這個專案主要用來記錄如何在 golang 裡面寫測試,並且整合 [Travis CI](https://travis-ci.org/) 和 [codecov](https://codecov.io)。 5 | 6 | [2016-09-21] 記錄如何在 test 中使用 Example function,並與 godoc 結合使用。 7 | 8 | ## 目錄 9 | 10 | - [撰寫 function](#function) 11 | - [撰寫 測試](#testing) 12 | - [撰寫 壓力測試](#benchmark) 13 | - [整合 Travis CI](#travis) 14 | - [整合 codecov](#codecov) 15 | - [撰寫 Example function](#example) 16 | - [Reference](#reference) 17 | 18 | 19 | ### 撰寫 function 20 | 21 | 首先,假設我們寫了一個 Division 的 function 用來處理兩個浮點數相除,並且判斷除數為零的時候要拋出 error,可以這樣寫,並把這個檔案存成 `calculator.go`: 22 | 23 | ```go 24 | package function 25 | 26 | import "errors" 27 | 28 | func Division(a, b float64) (float64, error) { 29 | if b == 0 { 30 | return 0, errors.New("除數不可為零") 31 | } 32 | 33 | return a / b, nil 34 | } 35 | 36 | ``` 37 | 38 | ### 撰寫測試 39 | 40 | 接著我們要針對這個 function 進行測試,產生一個 `calculator_test.go` 的檔案來寫我們的測試程式。在 go 中,每一個檔案只要加上 `_test` 就是對應的測試程式。 41 | 42 | ```go 43 | package function 44 | 45 | import "testing" 46 | 47 | func Test_Division(t *testing.T) { 48 | if i, e := Division(6, 2); i != 3 || e != nil { 49 | t.Error("Fail to pass") 50 | } else { 51 | t.Log("Pass") 52 | } 53 | } 54 | 55 | func Test_Division_Zero(t *testing.T) { 56 | if _, e := Division(6, 0); e == nil { 57 | t.Error("Should not go here.") 58 | } else { 59 | t.Log("Check zero correctlly.") 60 | } 61 | } 62 | ``` 63 | 64 | 在這裡我們要測試兩個情境: 65 | 66 | - 測試 6 / 2 要等於 3 67 | - 測試 6 / 0 會拋出 error 68 | 69 | 你可以先在 local 端使用 `go test -v ./function/` 指令進行測試: 70 | 71 | ``` 72 | $ go test -v ./... 73 | === RUN Test_Division 74 | --- PASS: Test_Division (0.00s) 75 | calculator_test.go:9: Pass 76 | === RUN Test_Division_Zero 77 | --- PASS: Test_Division_Zero (0.00s) 78 | calculator_test.go:17: Check zero correctlly. 79 | PASS 80 | ok GoTestExample/function 0.021s 81 | ``` 82 | 83 | 確定沒問題後,就完成基本的測試。`-v` 的參數代表開啟 debug mode,你可以看到每個測試運作的情形。 84 | 85 | ### 撰寫壓力測試 86 | 87 | golang 測試的項目還可以包含性能的測試,只要在設測試 function 中,以 `Benchmark` 開頭的開頭的 function 就代表一個壓力測試的側項。針對針對 Division 的函式,我們可以寫一個很簡單的測試函式如下: 88 | 89 | ```go 90 | func BenchmarkDivision(b *testing.B) { 91 | for i:=0 ; i 使用 Travis CI 進行自動測試 108 | 109 | 你可以在每次的 commit 時,透過 Travis CI 進行測試,整合的方式很簡單,首先,Travis CI 是透過 yaml 進行設定,在你的專案根目錄下,新增一個 `.travis.yml` 的檔案,寫入: 110 | 111 | ```yaml 112 | language: go 113 | 114 | sudo: false 115 | 116 | go: 117 | - 1.6 118 | - 1.7 119 | 120 | before_script: 121 | - go fmt ./... 122 | - go vet $(go list ./...) 123 | 124 | script: 125 | - go test ./function/ 126 | - go test ./function/ -bench=. -cpu=1,2,4,8 127 | 128 | ``` 129 | 130 | 這裡的意思是,讓 Travis CI 幫你測試 1.6 和 1.7 版的 golang 是不是可以正確跑測試。 131 | 132 | 在跑測試前,我們做 `go fmt` 和 `go vet` 兩件事情。 133 | - `go fmt` 會自動格式化好你的程式碼 134 | - `go vet` 則是會針對程式碼進行靜態分析,找出一些可能的潛在錯誤。 135 | 136 | 這部分你不寫,也是可以的,只是加上去會讓你在跑測試前,先透過 golang 自帶的工具幫你處理掉一些低級錯誤。 137 | 138 | 接著是測試的部分,我們主要執行兩行指令: 139 | - `go test ./function/`:我們主要測試 function 這個這個 package 裡面的函式。 140 | - `go test ./function/ -bench=. -cpu=1,2,4,8`:跑壓力測試。 141 | 142 | 接著你在 Travis CI 裡面,當你有新的 commit 時,就會看到你的專案正在進行測試: 143 | 144 | ![image](https://github.com/kevingo/blog/raw/master/screenshot/travis.png) 145 | 146 | ### 整合 codecov 147 | 148 | codecov 是用來檢查 code coverage 的一個線上服務,和 Travis CI 以及 github 都整合的很好,很容易就可以與你的專案整合起來了。 149 | 150 | 首先,你只需要將你的 `.travis.yml` 改成: 151 | 152 | ```yaml 153 | language: go 154 | 155 | sudo: false 156 | 157 | go: 158 | - 1.6 159 | - 1.7 160 | 161 | before_script: 162 | - go fmt ./... 163 | - go vet $(go list ./...) 164 | 165 | script: 166 | - go test -coverprofile=coverage.txt -covermode=atomic ./... 167 | 168 | after_success: 169 | - bash <(curl -s https://codecov.io/bash) 170 | ``` 171 | 172 | 在跑測試的時候,利用 go test 指令自帶的一個 `coverprofile` 多產生一個 `coverage.txt` 的檔案,並且指定 `covermode=atomic`。 173 | `covermode` 共有 `set`、`count` 和 `atomic` 三種模式,`atomic` 指定每個測試測試 statement 在並發的狀況下跑了幾次。最後,在測試成功後增加一行 `bash <(curl -s https://codecov.io/bash)`,就這麼簡單! 174 | 175 | 如果你登入到 [codecov.io](https://codecov.io),還可以看到 code coverage 的數據,也會顯示你的測試涵蓋了哪幾個部分,相當方便。 176 | 177 | ![image](https://github.com/kevingo/blog/raw/master/screenshot/codecov.png) 178 | 179 | ### Example function 180 | 181 | 在 testing 的 package 中,除了提供了以 Testxxx 開頭的 testing function 外,還提供了一種以 Example 開頭的 function。這種 Examplexxx 的 function 用途是提供 godoc 使用的範例程式碼。這種 Example function 有兩個規範: 182 | 183 | 1. 以 Example 開頭 184 | 2. 沒有參數 185 | 186 | 你可以在原本的 `_test.go` 的檔案中,增加一個 ExampleDivision 的 function,並且增加一個 `Output:` 的註解: 187 | 188 | ```go 189 | func ExampleDivision() { 190 | fmt.Println(Division(6, 2)) 191 | // Output: 3 192 | } 193 | ``` 194 | 195 | 接著,你可以跑一下測試 `go test ./function/` 確認沒問題: 196 | 197 | ``` 198 | $ go test -v ./function/ 199 | === RUN Test_Division 200 | --- PASS: Test_Division (0.00s) 201 | calculator_test.go:12: Pass 202 | === RUN Test_Division_Zero 203 | --- PASS: Test_Division_Zero (0.00s) 204 | calculator_test.go:20: Check zero correctlly. 205 | PASS 206 | ok github.com/kevingo/GoTestExample/function 0.023s 207 | ``` 208 | 209 | 你可能會好奇為什麼 Example 要跑測試?事實上,在跑測試的時候,如果你有寫 `Output:` 在註解中時,他會幫你檢查你的輸出是不是正確,如果不正確的話,是沒辦法跑過測試的。你可以嘗試更改上面註解中的值然後再跑跑看測試,會得到類似以下的錯誤: 210 | 211 | ``` 212 | got: 213 | 3 214 | want: 215 | 216 | FAIL 217 | exit status 1 218 | FAIL github.com/kevingo/GoTestExample/function 0.021s 219 | ``` 220 | 221 | 接著,golang 內建了 godoc 工具,可以在 local 端跑一個 web server 來看文件,你可以執行 `godoc -http=:6060` 指令來起一個跑在 6060 port 的 doc server。接著在網址列輸入:`http://localhost:6060/pkg/github.com/kevingo/GoTestExample/function/` (目錄名稱可以按照自己實際的專案目錄結構來調整),就會看到我們剛剛建立的 ExampleDivision 被轉換成 godoc 的文件了: 222 | 223 | ![image](https://github.com/kevingo/blog/raw/master/screenshot/godoc.png) 224 | 225 | ### References 226 | - [codecov example-go](https://github.com/codecov/example-go) 227 | - [The Go Blog - The cover story](https://blog.golang.org/cover) 228 | - [golang test package](https://golang.org/cmd/go/#hdr-Test_packages) 229 | --------------------------------------------------------------------------------