1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

go-cmpでprotobuf定義の構造体をdiff/equalするときにpanicになる時の対処

Last updated at Posted at 2021-02-01

ハマったのでメモ

助けられた

大変参考になりました。ありがとうございます :pray:
https://blog.rokiyama.dev/2020/09/11/go-cmp-diff-protobuf/

状況

golangでprotobuf+gRPCなサーバーサイドプログラムのテストを書いてる時に発症した。
テストでは、テスト対象の関数に渡す値と、期待される戻り値を最初に定義しておき、その2つの値(入・出)をgo-cmpで比較する方式を取っていた。👆のリンクではdiffを取っている例だけど、私の場合はEqualで再現した。

panic: cannot handle unexported field at {*package.StructName}.ChildStructName.state:
        "github.com/hoge/mypb/go/package/v1".Struct

結局protogenで生成したprotobuf messageのStruct内において、外部に出てない自動生成フィールド(小文字で定義されたもの)を比較できないことによって発生している模様。

type Name struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Id           uint32        `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
}

panicにも出てるように、この型のstateフィールドが引っかかってるらしい。

対策1

ブログにたどり着き、若干状況違う中で下記の通りテストを書き直したらpanicしなくなった

import(
	"google.golang.org/protobuf/testing/protocmp" // 追加でimportしておく
)

if !cmp.Equal(res, expectedResponse, opts, protocmp.Transform()) { // Equalに渡すパラメータに protocmp.Transform() を追加
	t.Fatalf("got %v want %v", res, expectedResponse)
}

対策2

go-cmpのオプション IgnoreUnexported を活用する
名の通り外に出てないフィールドを無視するための関数が用意されている

opts := cmpopts.IgnoreUnexported(package.ParentStruct{}, package.RelatedStruct1{}, package.RelatedStruct2{})
if !cmp.Equal(res, expectedResponse, opts) {
	t.Fatalf("got %v want %v", res, expectedResponse)
}

比較したいstructにおいて、unexportedなfieldが入っているものを全て渡しておくと良い。protocmp.Transform()に比べて、必要な分だけ無視できる良さがあるが関連するstructが多くなると列挙するのが面倒になってくる

1
1
0

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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?