LoginSignup
5
4

More than 3 years have passed since last update.

Go/EchoでProtocolBuffersを使ったE2Eテストを書く

Posted at

この記事はGo 4 Advent Calendar 2020 6日目の記事です。

業務でGo * ProtocolBuffersなプロダクトのE2Eテストを書くことがあっため、簡単に書かせていただきます。

はじめに

テストで利用した構成は以下の通りです。

・プログラミング言語:Golang 1.13.5
・Webフレームワーク:echo 
・DB:MySQL:

サンプルコード全体はこちらにご用意しましたのでをこちらをご参照ください。

今回、Webフレームワークとして、echoを利用していますが、
おそらく他のWebフレームワークを利用した場合でも同様にできるかと思います。

テスト対象コード

今回はごく簡単な構成としまして、echoでサーバーを起動して、アクセスするとレスポンスを返すようにしました。

main.go
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のファイルは以下です。

test.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

expectedactualを比較すると同じに見えるのですが。。
なぜこれがequalにならないのかわからよくわかっていないため、もしご存知な方いらしたら教えていただけると幸いです。

最後に

ProtocolBuffersのE2Eテストは変換する手間が発生しますが、
わかってしまえば特に苦戦することもなかったです。
(assert.Equal(t, want, res)で値が一致しないのは)

これはnet/http/httptestが使いやすく、簡単にテストサーバーを立てることができることが大きいかと感じました。

"net/http/httptest""testing"を使って色々テストを書いたので本当はそのノウハウも書きたかったのですが、今回時間がなく書けなかったのでまた次回書きたいと思います。

今回参考にさせていただいた記事

5
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4