この記事はAteam Hikkoshi Samurai Inc. & Ateam Connect Inc.(エイチーム引越し侍、エイチームコネクト) Advent Calendar 2019 12日目の記事になります。
はじめに
どーも、まいどおおきにおはこんにちこんばんは。
現在、エイチーム引越し侍では、2019年6月より某サイトのリプレイスに取り掛かっております。
リプレイスにあたりバックエンドの技術選定に関しては開発部全員で話し合いました。
案として挙がってきたのは、【PHPでLaravel】
、【RubyでRuby on Rails】
、【GoでClean Architecture】
あたりです。
そもそも、既存のサイトは【PHPのsymfony】
で作成されており、移行のことを考えるとPHPは有力とされておりましたが最終的な投票の結果、なんと【GoでClean Architecture】
になりました。
しかも次点は、【Ruby on Rails】
という、、、PHPには、苦い思い出があったのでしょうか。(想像はできますが)
その選定方法や、選定基準は今回割愛しますが、いずれ発表したいと思います。
さて、その【GoでClean Architecture】
に決定になったもののGO言語
の有識者が、当時一人しかいない中での選択で、私自身「びつくりした」というほかありません。
私も趣味程度しか触ったことがなく、GO言語
を実際に使う機会があるなんて、夢にも思いませんでした。
ただ、そのたった一人のGO言語
の有識者は2ヶ月ほどでリプレイスメンバーから離れてしまい、内心「大丈夫なのか!?」と思った次第です。
しかーーーーし、そんなことはなんのその、クリーンアーキテクチャの思想を取り入み、TDDを駆使しながらメンバーは着実に力をメキメキとつけていき、わたしは置いてけぼりをくらっております。
しかもしかもしかも、そのリプレイスによって「テストカバレッジ80%を達成する」という目標を抱いておりますので、なんとしても達成していきたいと思っています。
そんなメンバーに置いてけぼりをくらわない&少しでも力になれるように、ここに備忘録として「Go
でテストカバレッジ見える化」についてを記載します。
[参考] Go 1.13
バックエンドはAPIサーバーとして想定しているので、そのテストパッケージもついでにご紹介。
net/http/httptest パッケージ
net/http/httptest
はHTTPに関するテストをする際に便利なパッケージです。
net/http/httptest
パッケージを使用すると、特定のアドレスとポートをListen
させることなくWEBアプリケーションのテストができます。
簡単なAPIサーバーの例
/contact?name=gopher
と問い合わせすると、Hello, gopher
と返ってくるアプリケーションです。
これをテストする場合、go run
して立ち上げ、curl http://localhost:8080/contact?name=gopher
を実行したり、postman で叩いてテストすることも可能ですが、TCPポート:8080
がすでに利用されていたり、毎回毎回postmanで叩くのはめんどうですよね。
そんな場合は、net/http/httptest
を使うと便利です。
ローカルのループバックデバイスで動作するテスト用のサーバを立てることができます。
API サーバの例
// server.go
package main
import (
"fmt"
"log"
"net/http"
)
// Route はこのAPIサーバのルーティング設定をしている
func Route() *http.ServeMux {
m := http.NewServeMux()
m.HandleFunc("/contact", func(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
}
fmt.Fprintf(w, "Hello, %s", r.FormValue("name"))
})
return m
}
func main() {
m := Route()
log.Fatal(http.ListenAndServe(":8080", m))
}
httptest.Server
が便利なのは、http.Handler
インターフェイスを引数に渡せることです。
今回だと、func Route
が *http.ServeMux
を返すようにしました。
*http.ServeMux
は http.handler
のインターフェイスを満たしているため、そのままhttptest.NewServer
の引数として渡すことができます。
テスト用サーバーを利用したテスト
package main
import (
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
)
func TestRoute(t *testing.T) {
// テスト用サーバーの立ち上げ
ts := httptest.NewServer(Route())
defer ts.Close()
// テスト用サーバーのURLは、ts.URL で取得できる
res, err := http.Get(ts.URL + "/contact?name=gopher")
if err != nil {
t.Fatalf("http.Get faild: %s", err)
}
contact, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Fatalf("read from HTTP Response Body failed: %s", err)
}
expected := "Hello, gopher"
if string(contact) != expected {
t.Fatalf("response of /contact?name=gopher returns %s, want %s", string(contact), expected)
}
}
httptest.Server
はGo
のインターフェイスをうまく利用した使いやすい仕組みであり、テストのための便利なツールセットとなっています。
テストカバレッジ
Go
におけるテストカバレッジ計測ツールについてご紹介。
テストカバレッジ見える化をするためには、まず大元であるテストカバレッジ計測ツールや方法が必要ですよね。
カバレッジ取得方法
go test -cover
この際、カバレッジを測定するだけでなく、テストも同時に実行されます。
より詳しい結果の閲覧方法
「go test」で作成されたカバレッジプロファイル
go test -coverprofile=c.out

注釈付きソースコードを表示するWebブラウザーで確認する場合
go tool cover -html=c.out

Webブラウザーを起動する代わりにHTMLファイルを書き出し
go tool cover -html=c.out -o coverage.html
※Webブラウザーと同じ
各機能のカバレッジ率を標準出力に表示
go tool cover -func=c.out

最後にカバレッジ注釈付きの変更されたソースコードを生成するには
go tool cover -mode=set -var=CoverageVariableName main.go

テストカバレッジを得ることで、自身のライブラリのテスト計画を練るだけでなく、そのライブラリのテストの指針についても知ることができます。
よくテストされている箇所とテストのあまりされていない箇所を知ることは、テストの傾向を知ることになり、それは実装の指針を知ることにもなります。
テストのカバレッジによってテストの傾向を知り、アプリケーションで起き得るリスクを察知するために、活用できるかと。
テストカバレッジ見える化&CI
やっぱり、チームで運用・保守をやるなら、数字や見える化があった方がモチベーションもあがると思いますので、codecovを使って、カバレッジの見える化も対応したいですね。今回は、travis-ciで試してみました。
やりかたは、codecovを参考に。
# .travis.yml
language: go
go:
- 1.13.x
- tip
before_install:
- go get -t -v ./...
script:
- go test -race -coverprofile=coverage.txt -covermode=atomic
after_success:
- bash <(curl -s https://codecov.io/bash)



緑がテストを回している箇所で、赤の部分がテストコードを書いていない部分です。
このように、図や、テスト箇所がすぐに見れるようになると、とてもわかりやすいです。
https://travis-ci.org/andmorefine/go-coveralls
https://codecov.io/gh/andmorefine/go-coveralls
ソースコード
きちんと、テストカバレッジを見える化することができました。
カバレッジをうまく使って、ガシガシ、テストの戦略を練っていきましょう!!!!
[参考文献]
golang.org/pkg/testing
golang.org/pkg/net/http
golang.org/cmd/go
Go でコードカバレッジを取得する
Golangのtestify/assert 使えそうな関数まとめ
godoc.org/github.com/stretchr/testify/assert
codecov/example-go
お知らせ
エイチームグループでは一緒に活躍してくれる優秀な人材を募集中です。
興味のある方はぜひともエイチームグループ採用ページよりご応募ください!
(Webエンジニア詳細ページ)よりお問い合わせ下さい。
明日
明日のエイチーム引越し侍、エイチーム コネクトアドベントカレンダーの担当は、@futa_326さんです!
是非、お楽しみに!
ほなまた!