Go で テストカバレッジを見える化の巻


この記事は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.ServeMuxhttp.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.ServerGoのインターフェイスをうまく利用した使いやすい仕組みであり、テストのための便利なツールセットとなっています。

テストカバレッジ

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さんです!

是非、お楽しみに!

ほなまた!