2019/01/16 - 16:51

CircleCI LocalでGoとMySQLを連携させたテストを実行する。

:

eviry開発のtkです。

CircleCI 2.0からDockerを用いてローカル環境でテストやビルドを実行できるようになりました。
これを使って「MySQLと連携するGoアプリケーションをCircleCI Localでテストを実行する」というのを試してみたので共有します。

ファイル構成は以下の通り。

.
├── .circleci
│   └── config.yml
├── go.mod
├── go.sum
├── main.go
└── main_test.go

mysqlとの連携のためにjinzhu/gorm、テストのアサーションとしてstretchr/testifyを使用しました。
また、パッケージ管理はmodulesで行っています。

mysqlの設定として、rootユーザーにrootというパスワードを設定し、testというデータベースを作成しておきました。

まずはmain.go。
Userというモデルを定義し、gorm.AutoMigrate()を使ってテーブルを作成します。
usersテーブルに対して3つのレコードを登録し、これらをSelete()で取得します。

package main

import (
    “fmt”
    “github.com/jinzhu/gorm”
    _ “github.com/jinzhu/gorm/dialects/mysql”
)

type User struct {
    Id   int
    Name string
}

func Connect() (db *gorm.DB) {
    db, err := gorm.Open(“mysql”, “root:root@tcp(127.0.0.1:3306)/test”)

    if err != nil {
        panic(err.Error())
    }

    return
}

func Select(db *gorm.DB) (users []User) {
    db.Find(&users)
    return
}

func main() {
    db := Connect()
    db.AutoMigrate(&User{})

    db.Create(&User{Name: “tk”})
    db.Create(&User{Name: “abc”})
    db.Create(&User{Name: “def”})

    result := Select(db)
    fmt.Println(result)

    db.DropTable(&User{})
}

これを実行すると登録したレコードの情報が表示されます。

go run main.go
[{1 tk} {2 abc} {3 def}]

今回はここで使用したSelect()のテストを実装し、これをmain_test.goとします。
内容はほぼmain関数と同じですが、最後にassert.Equal()を使って結果の検証をしています。

package main

import (
    “github.com/stretchr/testify/assert”
    “testing”
)

func TestSelect(t *testing.T) {
    db := Connect()

    db.AutoMigrate(&User{})

    users := []User{{Name: “tk”}, {Name: “abc”}, {Name: “def”}}
    db.Create(&users[0])
    db.Create(&users[1])
    db.Create(&users[2])

    assert.Equal(t, users, Select(db), “Invalid Result”)

    db.DropTable(&User{})
}

さて、これをCircleCI Localを使い、ローカルでDockerコンテナ上のアプリとmysqlを連携してテストできるようにします。

今回はCircleCI公式のイメージを使用し、mysqlの環境設定を変数として渡しています。
CircleCI公式のイメージにはdockerizeがインストールされているので、これを使ってmysql用のコンテナが立ち上がるのを監視します。
こうしておかないとアプリ側が先に立ち上がり、DBの準備が完了する前にテストを実行して失敗します。
mysqlのコンテナへは「localhost」「127.0.0.1」をホストに指定して接続することが可能です。

コンテナの準備ができたらgo testを実行してテストします。

これらをまとめて.circleci/config.ymlを作ります。

version: 2
jobs:
  build:
    docker:
      - image: circleci/golang:1.11
      - image: circleci/mysql:5.7
        environment:
          MYSQL_DATABASE: test
          MYSQL_ROOT_PASSWORD: root
          MYSQL_ROOT_HOST: '%'

    working_directroy: /home/circleci/project

    steps:
      - checkout
      - run:
          name: Wait for db
          command: dockerize -wait tcp://localhost:3306 -timeout 1m -wait-retry-interval 5s

      - run:
          name: Run unit tests
          command: go test ./...

公式のGoアプリをビルドする場合のconfig.ymlを参考にしました。 -> Language Guide: Go - CircleCI

CircleCIをローカルで実行するにはコマンドが別途必要になるので、こちらを参考にインストールします。
また、goのライブラリを実行前に取得していると時間がかかるので、コンテナにローカル環境のものをマウントするようにしました。

circleci local execute --job build -v $GOPATH/pkg/mod:/go/pkg/mod

コマンドが実行されるとコンテナの準備から始まり、ソースコードの配置、MySQLの起動監視の後にテストが実行されます。
最後にgo testの実行結果が表示されれば完了です。

Dockerを使ってテストできるので、個人間での環境の差異を生じること無くテストすることが可能です。
また、この設定はCircleCIのサービス上でも動くので、AWS ECS/ECRと連携すればローカル・テスト・本番でほとんど差分のない状態で動作確認することができます。
このあたりはDockerを使える強みですね。

以上になります。