Synamon’s Engineer blog

Synamonでは、VR空間に複数人が同時に接続可能で、多彩な標準機能を搭載している『NEUTRANS』という独自のVRシステムを開発しています。ビジネスなどの現場でも使いやすいよう、独自の機能や技術を日々追究しています。このブログでは、『NEUTRANS』開発の裏側にあるVR技術と、それを支えるUnityやC#といった技術の話を書いていきます。

Goで作ったAPIでGithub Actionsを使ってAPIを直接叩くテストを回す

ごあいさつ

エンジニアのうぃすきー(@Whisky_shusuky)と申します。弊社ではインフラ・バックエンド・フロントエンドとWeb周りを全般的に対応しております。

最近Goをよく使っているため、以前公開したGinで作ったサンプルAPIにGithub Actionsの設定を追加してCIでtestが回るようにしてみましたのでこの場を借りて紹介しようと思います。

github.com

またサンプルAPIの中身については以前Qiitaに記載しましたので必要でしたらご確認ください。

qiita.com

テストの紹介

元々Railsを扱う機会が多かったので、Railsでのrequest specと同様にして実際にapiにアクセスしてレスポンス内容を検証するテストを実行することでAPIの動作を保証したいと思いました。 testfixturesを使えばテスト用DBにテストデータを以下のようにしてyaml形式で定義できます。

- id: 1
  shop_name: "test shop name"
  shop_description: "test shop description"

https://github.com/whisky-shusuky/gin-gorm-rails-like-sample-api/blob/master/test/testdata/fixtures/shops.yml

このデータを以下のようにして実際にAPIにアクセスして取得するテストを書きました

   t.Run("returns 200 when GET /", func(t *testing.T) {
        req, _ := http.NewRequest("GET", testServer.URL+"/api/v1/shops", nil)
        res, _ := client.Do(req)
        body, err := ioutil.ReadAll(res.Body)
        if err != nil {
            t.Error("[Error]", body, err)
        }

        assert.Equal(t, http.StatusOK, res.StatusCode)
        expectedBody := `{"shops":[{"id":1,"shop_name":"test shop name","shop_description":"test shop description"}]}`
        assert.JSONEq(t, expectedBody, string(body))
    })

https://github.com/whisky-shusuky/gin-gorm-rails-like-sample-api/blob/master/test/shop_request_test.go

Github Actions でテストを回す

上記のテストをgithub actionsで回るように設定しました。

name: test
on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]
jobs:
  golangci:
    services:
      mysql:
        image: mysql:5.7
        ports:
          - 3306:3306
        env:
          MYSQL_ALLOW_EMPTY_PASSWORD: yes
          BIND-ADDRESS: 0.0.0.0
        options: --health-cmd "mysqladmin ping -h localhost" --health-interval 20s --health-timeout 10s --health-retries 10
    strategy:
      matrix:
        go-version: [1.16.x]
        os: [ubuntu-latest]
    name: test
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v2
      - name: testing
        run: ENV_GO=citest bash -c 'go test ./... -v'

https://github.com/whisky-shusuky/gin-gorm-rails-like-sample-api/blob/master/.github/workflows/master.yml

ここではservicesのmysqlをデータベースとして使用しております。ローカルではdocker-composeで別途立てたコンテナを使用しているため接続方法が異なってきます。 そこでrun: ENV_GO=citest bash -c 'go test ./... -v'の形式で渡した環境変数ENV_GOを使って環境に応じて接続方法が変わるようにして対応しました。若干ハードコード気味ですがそれは置いておいて、こんな感じで環境変数に応じて動作を切り替えることが可能です。

       if env == "citest" {
            datasource = "root@tcp(127.0.0.1:3306)/"
            dsn = "root@tcp(127.0.0.1:3306)/sampletest?parseTime=true"
        } else {
            datasource = "root@tcp(db)/"
            dsn = "root@tcp(db)/sampletest?parseTime=true"
        }

これでPRをmaster向けに作るとGIthub Actionsが走ります f:id:Whisky_shusuky:20211011183711p:plain

むすび

Railsを元々書いていてGoを書き始めた方はrequest specを書きたくなると思います。そのような方の参考になりましたら幸いです。実際にAPIを叩くテストを書いたことが無い方も、APIの動作を担保するという意味では有効だと思いますので試してみるのはいかがでしょうか。