🌸

【Go】GraphQL(gqlgen) + Go(gin) + runn でテストをしてみる

2022/09/23に公開

はじめに

サーバーサイドの八木です。
娘が生まれてからアゴヒゲを一回もそっていなかったようでモサモサになってしまいました。
娘は喜んで引っ張っているので一旦はヨシとします(怒られたら謝ります)


先日、なんとなく Zenn を眺めていると以下の記事が目に入りました。

https://zenn.dev/katzumi/articles/api-scenario-testing-with-runn

そういえばAPIのシナリオテストしたいけど、丁度いいツールが見当たらず長らく手つかずになっていたプロジェクトがあり、導入する価値がありそうだなぁと思い試してみました。

https://github.com/k1LoW/runn

結果から言うと現在使用しています。導入して良かったと感じています 🎉

良かった点

  • go test で完結出来ること
  • API の呼び出し結果を取り回すことが出来る
  • テストケースだけYAMLに逃がすことが出来るのでメンテすべきファイルが明確になる
  • 機能がシンプルなので学習コストが低い
  • データベースへのクエリ発行も対応している

上記がポイントかと思います。
特に 「go test で完結」 + 「テストケースだけYAMLに逃がすことが出来る」 組み合わせは自分の好みに合っていてナイスでした。

これまでは Postman も使っていることもあり newmanDcoker で 頑張ったりしていたのですが、collection.json を更新する度にコンフリクトしたり、PR投げられたとてレビューが辛いなど小さいストレスがちょこちょこありました。

より詳しい解説は元の記事や以下の記事を参考にして頂いたら良いかと思うので、ここではタイトルに有る内容をソースコードベースで書き連ねていきます。

https://tech.pepabo.com/2022/06/07/scenario-testing-in-go/

といってもプロダクションのコードを載せるわけにはいかないので、以前作ったサンプルのソースに継ぎ足していきます。

ためしてみた

以前書いた記事で gin + GraphQL で試せそうな環境があったので試してみました。

https://zenn.dev/ygsn/articles/e047d74c5870ca

ソースコードはこちらです。

https://github.com/yag13s/gql-upload-sample

書いていて嬉しかった点

既存のコードでとりあえず試していたので余計なコードが多いですが、特に嬉しかったポイントだけピックアップします。

Go の testing に乗っかれるので CI も楽ちん

func Test_runn(t *testing.T) {
	t.Run("test runn with gin and GraphQL", func(t *testing.T) {
		// 略
		// 1. gin を初期化
		r := gin.Default()
		
		// 2. ルーティング設定も既存の処理を使い回せる
		defineRoutes(r)

		// 3. テストサーバの生成もほぼセオリー通りで可能
		ts := httptest.NewServer(r.Handler())
		t.Cleanup(func() {
			ts.Close()
		})

		opts := []runn.Option{
			runn.T(t),
			runn.Runner("req", ts.URL),
		}

		// 4. テストケースをガサッと読み込み
		o, err := runn.Load("../testutil/books/**/*.yml", opts...)
		if err != nil {
			t.Fatal(err)
		}

		ctx := context.Background()
		// 5. ここでYAMLで定義したテストケースが実行される
		if err := o.RunN(ctx); err != nil {
			t.Fatal(err)
		}
	})
}

圧倒的にかんたんで嬉しい👶

GraphQL のテストケースも可読性が良い

GraphQL と言っても HTTP 上の話なので 公式のREADMEに書いているサンプルを参考にすればかんたんにテストケースを作成できます。

desc: test example
vars:
  queryExampleResponse: "json://response/example.json"
debug: true
steps:
  exapmle:
    desc: example query
    req:
      /query:
        post:
          body:
            application/json:
              query: "query {
                          example {
                              envcode
                              projectName
                              version
                          }
                      }"
    test: |
      current.res.status == 200
      && current.res.body == vars.queryExampleResponse

抜粋したこちらのケースでは

  • /query に POST でリクエスト
  • 返ってくるステータスコードが 200 であること
  • 予め response/example.json に定義していた JSON とレスポンスがあっていること

をテストしています。

debug オプションを true にしておけばエラーになったときのリクエスト・レスポンスが詳しく確認できるのも嬉しいポイントです。

こんな感じでテストケースをズンガズンガ🏃と作っていき go test で実行できるのはとても精神衛生上良いです。
少し複雑なテストを書きたいときでも、テスト関数から分けたりしながらテストを使い回すなど夢が広がります。

また response/example.json など外部に定義した JSON を取り込めるのは地味に嬉しいです。

"{\n \"data\":\n {\"example\": {\n \"envcode\": \"test\",\n \"projectName\": \"gql-upload-sample\",\n \"version\": \"testing\"\n }\n }\n }"

誰もこんなの読みたくないしメンテも辛いと思います。
少なくとも自分はバックスラッシュの嵐を見ると視力が悪くなります👀

DB へのクエリのサポート

今回は勢いのまま記事を書いたのでサンプルには用意できませんでしたが、こちらがとても強力な機能だと思っています👏
例えば、テストデータを予め用意している場合「テストケースとテストデータで2重管理するの嫌だなぁ」と思うときがあると思います。
そんなときに、テストデータさえきちんと用意していれば嬉しくなれそうな予感がしてます。というかプロダクションのテストではすでに嬉しいことになっています。

さいごに

runn は機能がシンプルであることで導入しやすかったです。
「全部入りモリモリパッケージ」を頑張って使おうとすると、最初は楽しいのですが思いがけないところでハマったりするので、困った時点で「違う選択肢を選ぶ」とか「機能を足してもらえるように頑張る」とかのほうが良いのかなぁと思ったりしてます。(なので日々のキャッチアップや知識の幅を広げるのが重要)

こういったツールを作ってくれる方がいるので日々感謝しないといけないと同時に積極的にコントリビュートしていきたいなぁと思います。

嬉しかったので14個も「嬉し」という単語を使ってしまいました。

Discussion