以前の記事(Golang と Line Notify を利用して API の証明書期限切れチェック)を作りましたが今回は単体テストを追加します。
Golang の単体テストとは
ざっくり調べた感じだと、以下の感じで作ってみたら良さそう
- テスト対象のコードと同じ階層に置く(標準パッケージが同じ構造になっていた)
- testing パッケージを利用する
- テスト用の関数のプレフィックスに「Test」をつける
- Assert が用意されていないため、自分で条件を記載する
(テストケースを失敗させたいときは「testing.T.Error」や「testing.T.Fatal」を利用する) - testing.T.Error:以降の処理を継続して実行される
- testing.T.Fatal:以降の処理が実行されない
テストコードを記載
テスト対象のコード
apichecker.go
package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"strings"
"time"
)
func main() {
var endpoint = flag.String("endpoint", "", "check target Endpoint URL")
var lineToken = flag.String("token", "", "LINE notify token")
flag.Parse()
var apiResult = getAPI(*endpoint)
var result = postLINE(*lineToken, apiResult)
fmt.Printf("LINE Post result [%t]\n", result)
}
func getAPI(endpoint string) string {
if endpoint == "" {
log.Println("not endpoint")
return "not endpoint"
}
var result = ""
resp, err := http.Get(endpoint)
if err != nil {
result = fmt.Sprintf("NG\n%s", err)
} else {
defer resp.Body.Close()
expire := "-"
if len(resp.TLS.PeerCertificates) > 0 {
expireUTCTime := resp.TLS.PeerCertificates[0].NotAfter
expireJSTTime := expireUTCTime.In(time.FixedZone("Asia/Tokyo", 9*60*60))
expire = expireJSTTime.Format("06/01/02 15:04")
}
result = fmt.Sprintf("OK (expire=%s)\n%s", expire, endpoint)
}
return result
}
func postLINE(token string, message string) bool {
if token == "" {
log.Println("not token")
return false
} else if message == "" {
log.Println("not text")
return false
}
data := url.Values{"message": {message}}
r, _ := http.NewRequest("POST", "https://notify-api.line.me/api/notify", strings.NewReader(data.Encode()))
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
resp, err := http.DefaultClient.Do(r)
if err != nil {
log.Println(err)
return false
}
defer resp.Body.Close()
_, err = ioutil.ReadAll(resp.Body)
if err != nil {
log.Println(err)
return false
}
return true
}
getAPI のテストコードを書いてみる
同一パッケージなので、private な関数もテストできて便利
以下のパターンのテストケースを用意
- パラメータエラー
- 正常に実行完了
- 証明書エラー
apichecker_test.go
package main
import (
"regexp"
"testing"
)
func TestGetAPI_パラメーターエラー(t *testing.T) {
if getAPI("") != "not endpoint" {
t.Error("failed validation check")
}
}
func TestGetAPI_正常(t *testing.T) {
r := regexp.MustCompile("OK \\(expire=[0-9]{2}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}\\)\nhttps://www.yahoo.co.jp")
if !r.MatchString(getAPI("https://www.yahoo.co.jp")) {
t.Error("function format error")
}
}
func TestGetAPI_証明書エラー(t *testing.T) {
r := regexp.MustCompile("NG\n.+")
if !r.MatchString(getAPI("https://www.yahoo.jp")) {
t.Error("function format error")
}
}
テストを実行
$ go test .
ok github.com/ynozue/apichecker 0.684s
CI にも適用する
設定ファイルにテストの実行を追加
.travis.yaml
language: go
install:
- go get -u golang.org/x/tools/cmd/goimports
- go get -u github.com/golang/lint/golint
script:
- go vet ./...
- diff <(goimports -d .) <(printf "")
- diff <(golint ./...) <(printf "")
- go test .
修正した内容を Git へ Push
$ go vet ./...
The command "go vet ./..." exited with 0.
$ diff <(goimports -d .) <(printf "")
The command "diff <(goimports -d .) <(printf "")" exited with 0.
$ diff <(golint ./...) <(printf "")
The command "diff <(golint ./...) <(printf "")" exited with 0.
$ go test .
ok github.com/ynozue/apichecker 2.600s
The command "go test ." exited with 0.