この記事はGo 4 Advent Calendar 2020 6日目の記事です。
業務でGo * ProtocolBuffersなプロダクトのE2Eテストを書くことがあっため、簡単に書かせていただきます。
はじめに
テストで利用した構成は以下の通りです。
・プログラミング言語:Golang 1.13.5
・Webフレームワーク:echo
・DB:MySQL:
サンプルコード全体はこちらにご用意しましたのでをこちらをご参照ください。
今回、Webフレームワークとして、echo
を利用していますが、
おそらく他のWebフレームワークを利用した場合でも同様にできるかと思います。
テスト対象コード
今回はごく簡単な構成としまして、echo
でサーバーを起動して、アクセスするとレスポンスを返すようにしました。
package main
import (
"net/http"
pb "github.com/DAdDY0055/go_protocol_buffers_e2e_test/proto"
"github.com/labstack/echo/v4"
"google.golang.org/protobuf/proto"
)
func main() {
e := echo.New()
e.GET("/hello", func(c echo.Context) error {
hello := &pb.Hello{Hello: "Hello, World!"}
data, _ := proto.Marshal(hello)
return c.Blob(http.StatusOK, "application/protobuf", data)
})
e.Logger.Fatal(e.Start(":8080"))
}
ProtocolBuffersを使っておりますので、変換に使うProtoのファイルは以下です。
syntax = "proto3";
package protofiles;
message Hello {
string hello = 1;
}
上記ファイルを作成した後、protoc
コマンドを使ってgoのシリアライズ・デシリアライズ用ファイルを生成します。
% protoc --go_out=./proto/ ./test.proto
※protocコマンドをインストールしていない場合は以下で可能です。
% brew install protobuf
テストコード
テストの実行には、
- テスト用サーバーの起動:
net/http/http/test
- 値の検証:
github.com/stretchr/testify/assert
を利用しました。
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
pb "github.com/DAdDY0055/go_protocol_buffers_e2e_test/proto"
"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/proto"
)
func TestHelloHandler(t *testing.T) {
// 想定する値を定義
want := &pb.Hello{
Hello: "Hello, World!",
}
router := NewRouter()
req := httptest.NewRequest("GET", "/hello", nil)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
assert.Equal(t, http.StatusOK, rec.Code)
res := &pb.Hello{}
proto.Unmarshal(rec.Body.Bytes(), res)
// assert.Equal(t, want, res) そのまま比較すると一致しない
assert.Equal(t, want.Hello, res.Hello)
fmt.Println("res", res) // 味気ないので出力
}
ここで鍵となるのは、ProtocolBuffersはバイナリで通信をしているため、assertでチェックをする前に値を変換する必要があるという点です。
proto.Unmarshal
でデシリアライズ変換した値を使うことで、assert
で検証できます。
実際にテスト実行
テストファイルがあるディレクトリにて、以下で実行します。
go test -v
実行結果
-> % go test -v
=== RUN TestHelloHandler
res hello:"Hello, World!"
--- PASS: TestHelloHandler (0.00s)
PASS
ok github.com/DAdDY0055/go_protocol_buffers_e2e_test 0.019s
この通り、値が合っていた場合OKとなります。
ちなみにですが、assert.Equal(t, want, res)
と直接比較したい気がしますが、その場合はNot equal
となりFAILとなります。
-> % go test -v
=== RUN TestHelloHandler
res hello:"Hello, World!"
--- FAIL: TestHelloHandler (0.00s)
main_test.go:30:
Error Trace: main_test.go:30
Error: Not equal:
expected: &protofiles.Hello{state:impl.MessageState{NoUnkeyedLiterals:pragma.NoUnkeyedLiterals{}, DoNotCompare:pragma.DoNotCompare{}, DoNotCopy:pragma.DoNotCopy{}, atomicMessageInfo:(*impl.MessageInfo)(0xc0000da640)}, sizeCache:0, unknownFields:[]uint8(nil), Hello:"Hello, World!"}
actual : &protofiles.Hello{state:impl.MessageState{NoUnkeyedLiterals:pragma.NoUnkeyedLiterals{}, DoNotCompare:pragma.DoNotCompare{}, DoNotCopy:pragma.DoNotCopy{}, atomicMessageInfo:(*impl.MessageInfo)(0xc0000da640)}, sizeCache:0, unknownFields:[]uint8(nil), Hello:"Hello, World!"}
Diff:
Test: TestHelloHandler
FAIL
exit status 1
FAIL github.com/DAdDY0055/go_protocol_buffers_e2e_test 0.019s
expected
とactual
を比較すると同じに見えるのですが。。
なぜこれがequalにならないのかわからよくわかっていないため、もしご存知な方いらしたら教えていただけると幸いです。
最後に
ProtocolBuffersのE2Eテストは変換する手間が発生しますが、
わかってしまえば特に苦戦することもなかったです。
(assert.Equal(t, want, res)
で値が一致しないのは)
これはnet/http/httptest
が使いやすく、簡単にテストサーバーを立てることができることが大きいかと感じました。
"net/http/httptest"
と"testing"
を使って色々テストを書いたので本当はそのノウハウも書きたかったのですが、今回時間がなく書けなかったのでまた次回書きたいと思います。