🤖

Goの単体テストとGitHubActionsでビルド・テストしてみる

2023/04/16に公開

概要

こちらの続き。
単体テストとGitHubActionsでビルド・テストを実行してみる。

環境

M1 Mac
docker desktop 4.17.0

まずはローカルでの構築

事前準備

今回はDB接続(infrastructure)モジュールのテストをするので、コンテナ上のMysqlにデータ登録する。
なので、コンテナ起動時にDDL実行するように↓のsqlファイルを用意しておく。

init.sql

SET
  GLOBAL sql_mode = 'NO_ENGINE_SUBSTITUTION';

SET
  session sql_mode = 'NO_ENGINE_SUBSTITUTION';

DROP TABLE IF EXISTS users;

CREATE TABLE `users` (
  `id` int NOT NULL AUTO_INCREMENT,
  `user_name` varchar(255) NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `deleted_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;

INSERT INTO testdb.users
(id, user_name, created_at, updated_at, deleted_at)
VALUES(1, 'test', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL);

docker-compose.yml

version: '3.8'
services:
  app:
    build:
      context: .
      target: dev
      dockerfile: Dockerfile
    env_file: .env
    tty: true
    container_name: gin-api
    depends_on:
      - db
    volumes:
      - ./:/go/src/app
    ports:
      - 8080:${SERVER_PORT}
      - 2345:2345
  db:
    image: mysql:8.0
    platform: linux/arm64
    tty: true
    container_name: ${MYSQL_HOST}
    env_file: .env
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
      TZ: ${MYSQL_TZ}
    ports:
      - 3306:${MYSQL_PORT}
    volumes:
      - data-mysql:/var/lib/mysql
      - ./db/mysql/log:/var/log/mysql
      - ./db/mysql/etc/my.cnf:/etc/mysql/conf.d/my.cnf
      - ./db/mysql/init:/docker-entrypoint-initdb.d → ここでsqlファイルの場所を記述
volumes:
  data-mysql:

テストモジュール

user_repository_test.go
ファイル名は末尾に_testをつけるらしい。
テストの関数名はググったもの参考に。(参考になる記事はたくさんありました)
とりあえず、動かしたいので、2ケースだけ作成。

・ケース1
userテーブルのID=1のデータがあるかチェック

・ケース2
userテーブルのID=2のデータがないかチェック(なければOK)

package mysql

import (
	"go-gin-api/config"
	"testing"
)

func TestMain(m *testing.M) {
	m.Run()
}

func Test(t *testing.T) {
	Connect(config.Config.MysqlHost, config.Config.MysqlUser, config.Config.MysqlPass, config.Config.MysqlPort, config.Config.MysqlDbName)
	db, _ := DB.DB()
	defer db.Close()

	ur := NewUserRepository(db)

	data1, err := ur.FindByID(1)

	if err != nil {
		t.Errorf("Calc() error = %v, wantErr %v", err, "")
		return
	}

	if data1.ID != 1 {
		t.Errorf("func FindByID = %v, want %v", data1.ID, "1")
	}

	data2, err := ur.FindByID(2)

	if err != nil {
		t.Errorf("Calc() error = %v, wantErr %v", err, "")
		return
	}

	if data2.ID != 0 {
		t.Errorf("func FindByID = %v, want %v", data2.ID, "0")
	}

}

動作確認

まずはコンテナ起動
(コンテナ上でMysqlもGoも動かす)

docker-compose up -d

テスト実行
-vオプションつけるとログを詳細に出してくれるらしい。
実行対象に./...を指定すると配下のディレクトリのテストをすべて実行してくれるらしい。

docker container exec -it gin-api go test -v ./...

テスト通った。

GitHubActions

ローカルでは動かせるようになったので、今度はGitHubActionsで動かせるようにしていく。
大まかには↓の流れになるように構築する。

  1. Mysqlのコンテナ起動
  2. ソースコードをチェックアウト
  3. DDL実行(テストデータ投入)
  4. ビルド
  5. テスト実行

準備

GitHubActionsは↓のようにworkflowsディレクトリ配下にymlファイルを配置しておくことで、任意のイベント(pushなど)発生時に自動的に記述したワークフローを実行してくれる。

.github/workflows/*.yml

なので、ディレクトリとymlファイルを作成。

今回は

.github/workflows/goci.yml

とする。

ymlの記述

今回記述したymlはこんなかんじ。
ほとんどはGitHubの公式ドキュメントの記述を参考にした。
何度か動かしつつ、トライ&エラー(環境変数の設定や読み込みでつまづいた)を繰り返した。
ユーザ名やパスワードなどはベタ書きしてしまっているが、シークレットに設定しておいてそこから読みこむことも可能。(今回はそこまではやらない)

name: Go Build and Test

on:
  push: 
      branches:
      - 'main'
      - 'releases/**'
      - 'develop'
  pull_request:
    types:
      - closed
    branches:
      - 'main'
      - 'releases/**'
      - 'develop'

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    services:
      mysql:
        image: mysql:8.0
        ports:
          - 3306:"3306"
        env:
          MYSQL_ROOT_PASSWORD: testpassword1
          MYSQL_DATABASE: testdb
          MYSQL_USER: testuser
          MYSQL_PASSWORD: testpassword2
          MYSQL_TZ: Asia/Tokyo
        options: --health-cmd "mysqladmin ping -h 127.0.0.1" --health-interval 20s --health-timeout 10s --health-retries 10
    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Set up Go
        uses: actions/setup-go@v3
        with:
          go-version: "1.20"

      - name: Copy DDL file
        run: cp db/mysql/init/init.sql ${{ github.workspace }}

      - name: Execute DDL
        run: |
          mysql -h 127.0.0.1 --port 3306 -uroot -ptestpassword1 -D testdb  < db/mysql/init/init.sql

      - name: Install dependencies
        run: go get . && go mod tidy

      - name: Build the code
        run: go build -v ./...

      - name: Run tests
        run: go test -v ./...
        env:
          SERVER_PORT: 8080
          MYSQL_HOST: localhost
          MYSQL_DATABASE: testdb
          MYSQL_USER: testuser
          MYSQL_PASSWORD: testpassword2
          MYSQL_PORT: 3306
          MYSQL_TZ: Asia/Tokyo

動作確認

mainにpushすると自動的に定義したワークフローが実行される。

少し待つと終わった。

テスト通ったみたい。

感想

とりあえず動くものを、、、ということでお試しで実装してみたが、Goは標準ライブラリが充実していて使いやすい。
GithubActionsもクセもなく、けっこう直感的にやりたいこと書けた。
リファクタリングはまた今度やろうと思う。(やぶん、やらない)

Discussion