はじめに
こんにちは
コンテナを使ったアプリケーション開発もぼちぼち増えてきたように感じます。
その中でも開発環境において、複数のコンテナ・プロセスを管理したいためにdocker-composeを利用している方も多いのではないでしょうか。
今回は、開発環境で利用しているdocker-composeをそのまま利用して、
CircleCIで自動テストする一例をご紹介したいと思います。
コンテナ構成
version: '2'
services:
db:
build:
context: db/
expose:
- "3306"
environment:
- MYSQL_ROOT_PASSWORD=root #rootパスワードの設定
- MYSQL_DATABASE=test
- MYSQL_USER=user
- MYSQL_PASSWORD=password
volumes:
- ./db/mysql_data:/db/mysql_data
ports:
- "3306:3306"
app:
build:
context: app/
volumes:
- ./app/:/go/src/app
command: realize start --run --no-config # DEBUG
restart: always
ports:
- "8080:8080"
depends_on:
- db
今回作成するAPI
GETだけできるとてもシンプルなものにします。
Status Code: 200
リクエスト
GET /user/1
レスポンス
{
"name": "山田",
"age": 11
}
Status Code: 404
リクエスト
GET /user/3
レスポンス
{
"code": "Not Found",
"message": "レコードが見つかりません"
}
対応するテーブル・スキーマ
desc users:
Field, Type, Null, Key, Extra
---------------------------------------
id , int(11), No, PRI, auto_increment
name, varchar(255), YES
age, int(11), YES
実装コード
r.GET("/user/:id", func(c *gin.Context) {
userId, _ := strconv.Atoi(c.Param("id"))
db := getDB()
var user model.User
if err := db.Where("id = ?", userId).First(&user).Error; gorm.IsRecordNotFoundError(err) {
c.JSON(http.StatusNotFound, gin.H{
"code": "Not Found",
"message": "レコードが見つかりません",
})
return
}
c.JSON(http.StatusOK, user)
defer db.Close()
})
※Webフレームワークにgin, ORMにgormを採用しています。
マッピングしているUser構造体
type User struct {
Id int `json:"-"`
Name string `json:"name"`
Age int `json:"age"`
}
テストコード
200と404を確認します。
テストレコードの作成もテストコードの中でやってしまいます。
※headerのチェックのみの簡略的なものとします。
func Test_Main(t *testing.T) {
db, err := gorm.Open("mysql", "user:password@tcp(db:3306)/test?charset=utf8mb4&parseTime=True&loc=Local")
if err != nil {
panic(err.Error())
}
// make db scheme
db.AutoMigrate(&model.User{})
// insert test record
db.Create(&model.User{Id: 1, Name: "山田", Age: 11})
t.Run("Check HTTP 200", func(t *testing.T) {
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodGet, "/user/1", nil)
req.Header.Set("Content-Type", "application/json")
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
})
t.Run("Check HTTP 404", func(t *testing.T) {
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodGet, "/user/2", nil)
req.Header.Set("Content-Type", "application/json")
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
})
}
CircleCIのconfig.yml
ポイントは、
buildタイプにmachineビルドを選択し、imageにcircleci/classic:edgeを選択する点です。
version: 2
jobs:
build:
machine:
image: circleci/classic:edge
working_directory: ~/repo
steps:
- checkout
- run:
name: install docker-compose
command: |
curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` > ~/docker-compose
chmod +x ~/docker-compose
sudo mv ~/docker-compose /usr/local/bin/docker-compose
- run:
name: docker-compose up
command: |
set -x
docker-compose up -d
- run:
name: test
command: docker-compose exec app go test -v main_test.go main.go
- run:
name: docker-compose down
command: docker-compose down
参考
-
Choosing an Executor Type
-
Installing and Using docker-compose
build結果
$ #!/bin/bash -eo pipefail
docker-compose exec app go test -v main_test.go main.go
^@^@[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /user/:id --> command-line-arguments.Router.func1 (3 handlers)
=== RUN Test_Main
=== RUN Test_Main/Check_HTTP_200
[GIN] 2020/01/25 - 07:51:33 | 200 | 2.23664ms | | GET /user/1
=== RUN Test_Main/Check_HTTP_404
[GIN] 2020/01/25 - 07:51:33 | 404 | 1.848185ms | | GET /user/2
--- PASS: Test_Main (0.03s)
--- PASS: Test_Main/Check_HTTP_200 (0.00s)
--- PASS: Test_Main/Check_HTTP_404 (0.00s)
PASS
ok command-line-arguments 0.040s
細かいところはソースをご覧下さい。
注意点
image: circleci/classic:edgeはローカル環境でpullすることができないため(2020/01/25時点)、ローカルでの動作確認はコンテナにアタッチして手動で行う必要があります。要は、
ローカルコマンド
$ circleci build
が必ずコケて使えないということです。
※おそらくCircleCI側の都合だと思います。#リソース制限など
おわりに
Pull Requestの段階で強制的に自動テストが走るようにすることは、もはやチーム開発において必須の仕組みだと感じました。
これからもCircleCIなどのCIツールを活用して、アプリケーションの品質向上と将来的な開発速度の向上を目指したいと思います。
CircleCIを活用したおもしろい例があれば、ぜひご紹介下さい。