🏃

テストツールrunnをGitHubActionsで動かしてみる(with docker-compose)

2024/04/26に公開

はじめに

こんにちは。株式会社トリドリでバックエンドエンジニアをしている松田です。
最近Golangを使って開発をしており、その際統合テストツールとして「runn」というものを採用し実装をしていました。
runnの公式ドキュメントを見ながら開発を進めていましたがdocker-composeを使った例が少なかったため、今回は同じような状況で実装をされている方の参考になればと思い記事を書くことにしました。

取り組んだことは多くありましたが長くなるので、実際に動かしながら試せる小さな範囲で書きました。よかったら手元で動かしてみてください!

この記事で扱うこと

  • テストツール「runn」の概要説明
  • GitHub Actionsの概要説明
  • runnの動かし方
  • docker-composeを使用してrunnをGitHub Actionsで動かす方法

達成したいこと

  • docker-composeを使ってGitHubActionsのrunner上でDockerコンテナを立ち上げrunnテストを実行すること

runnとは

runnとはgo言語で書かれたオペレーション自動化ツール・パッケージです。ユースケースはいくつか想定されますが、開発者の言葉をお借りすると「APIシナリオテスティングツール・パッケージ」として主に使用されています。

ドキュメント
Github
https://github.com/k1LoW/runn

クックブック
https://zenn.dev/k1low/books/runn-cookbook

GitHub Actionsとは

https://github.com/features/actions

GitHubの機能の一つで、事前に定義した処理と条件を書いたワークフローを自動実行することができる機能です。CI/CDのための機能としてよく使われている機能です。
今回はGitHubActionsを使用してrunnを動かす例をいくつか挙げていきます。

Dockerコンテナを立てて、ローカルでrunnを動かす

まずはrunnの基本的な使い方の理解も含め、簡単な例を用いてローカルでrunnを動かしてみます。runnはGo製のテストツールなのでGoで環境を作って試していくことにします。

GoアプリとDockerコンテナの環境構築

ターミナル
$ mkdir runn-practice
$ cd runn-practice
$ go mod init test-app // test-appの部分はGithubのリポジトリurlなどが良いが、お試し用であればなんでもok
// 実行結果
go: creating new go.mod: module my-app
$ ls
go.mod // go.modファイルが出来上がります

// main.goの作成
$ touch main.go
main.go
package main

import (
	"fmt"
	"net/http"
)

func main() {
	http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello, World!")
	})

	http.ListenAndServe(":80", nil)
}

そしてDockerの環境も作っておきます。

Dockerfile
FROM golang:1.22.2

RUN go install github.com/cosmtrek/air@latest
RUN go install github.com/k1LoW/runn/cmd/runn@latest

WORKDIR /app

CMD ["air"]

EXPOSE 80

airをインストールしていますが、こちらはホットリロードを実現するためのツールです。ローカルファイルの編集を検知して自動でbuild→コンパイルしてくれるので開発時に便利。

airについてはこちら
https://github.com/cosmtrek/air

air.towlという設定ファイルも追加しておきましょう。
以下の公式の設定そのまま貼り付けで問題なしです⚪︎
https://github.com/cosmtrek/air/blob/master/air_example.toml

docker-compose.yml
version: "3.9"

services:
  app:
    build:
      context: ./app
      dockerfile: ./Dockerfile
    volumes:
      - ./app:/app
    ports:
      - "8080:80"

ディレクトリ構造は以下の通り。

ターミナル
$ find .
.
./app
./app/.air.towl
./app/go.mod
./app/Dockerfile
./app/main.go
./docker-compose.yml

準備ができたのでコンテナ立ち上げ

ターミナル
$ docker compose up -d

コンテナが立ち上がったら起動を確認します。ブラウザからlocalhost:8080/healthでリクエストしても良いですし、ターミナルでcurl localhost:8080/healthでも良いです。Hello, World!%と出力されればokです🙆‍♂️

ローカルでrunnを動かす

runnのテストファイルはrunbook(ランブック)と呼ばれています。
このrunbookの書き方は、方言のように少しインデントを変えても動くことが多いです。以下は一例です。

desc: runnにてhttpリクエストのテストを行う
runners:
  req:
    endpoint: http://127.0.0.1:80
steps:
  - 
    desc: health check
    req:
      /health:
        get:
          body: null
    test: |
      current.res.status == 200

dockerコンテナ内で動くアプリサーバー自身に対して/healthのパスでリクエストしてそのレスポンスステータスを確認するという例になっています。
desc・・・runbookの説明づけ
runners・・・runbook内で実行する各種Runnerを定義。今回はHTTP runnerを定義。
steps・・・steps以下にテストしたい項目を書き連ねていきます。runnersで指定したreqにstepsのreqが繋がっていきます。testの項目で検証したい項目を書きます。

このrunbookをターミナルから動かしてみましょう!runbookの実行コマンドは run runn <runbookファイル名> となります。デバッグ情報が欲しい場合は --debug をつけましょう。
今回はdockerコンテナで動かしているのでdocker exec <コンテナ名> から始まっています。

$ docker exec runn-practice-app-1 runn run runn-test.yml
.

1 scenario, 0 skipped, 0 failures

このようになればテストがパスしています!

docker-composeを使用してrunnをGitHubActionsで動かす方法

では上記のrunbookを、今度はGitHub Actionsで動かしてみましょう。
docker-composeを使った例はあまりなかったので、まずは一旦試して動作をみてみることから始めました。

GitHubActionsで使用するworkflowファイルを用意

ターミナル
$ mkdir .github && mkdir .github/workflow
$ touch .github/workflow/runn-test.yml
runn-test.yml
name: runn test

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Docker
        run: docker-compose -f docker-compose.yml up -d

      - name: Check Docker status
        run: docker-compose ps

      - name: Runn test
        run: docker-compose exec runn-practice_app_1 runn run runn-test.yml
      
      - name: Clean up
        if: always()
        run: docker-compose -f docker-compose.yml down
  • まずはリポジトリのコードをチェックアウトしてきて、docker-composeを動かします。
  • 一応docker-compose psで起動後の確認も入れています。消してもokです。
  • その後GitHubActions上で動いているrunner上にdockerコンテナが立ち上がったので、立ち上がったコンテナに対してrunnコマンドを実行します。
  • 最後にコンテナを落として終了という流れです。

これでGitHubActionsでテストがパスするはずです!

GitHubActionsのrunner上でDockerコンテナを定義することもできますが、ローカルで用意したdocker-compose.ymlをそのまま使用してコンテナ環境を作ることでCI上と開発との環境差分ができくくおすすめです。

その他、実装時に詰まった点もありましたので、以下に記しておきます。

  • 業務で書いたコードでは、appの他dbのコンテナもあり、dbコンテナの後appコンテナを立ち上げていたのですが、appコンテナが立ち上がってすぐにrunnコマンドを実行すると接続先がありませんというエラーが出たこと。これはコンテナは立ち上がったが、その直後にはリクエストを受け付けられない状態であったため接続できずにいました。こちらは少し時間を置くためにwaitを挟んで対応しました。

  • 書いているコードが、どの場所で何に対して処理を行なっているかが分からなくなりがちだったこと。具体的には、以下の図のようにGitHubActionsのrunners上でDockerコンテナ(app, db)を立ち上げている状態で、appコンテナへのリクエストの際のポートは?appからdbへのリクエストはどう指定する?などです。

app→dbへは、docker-composeでのマッピング通り(runnersが8080, appが80)
appが自身を指すときは127.0.0.1:80(:80は書かなくても良い)
app→dbへは同一ネットワークであるため(デフォルトで同一ネットワークになる)サービス名だけでリクエストできる。

もっと良いコードにできる点はたくさんあると思います。良いアイデアがありましたらぜひ教えてください。

まとめ

  • docker-composeを使ってGitHub Actions上のrunnerでdockerコンテナを立ち上げrunnのテストを動かしてみました。
  • コンテナの立ち上げ順序やポートマッピング、実行コマンドなど細かくステータスを意識しつつ指示を書くことが苦戦しやすいので注意点

runnは慣れれば使い勝手が良いテストツールなので、皆さんもぜひ使ってみてください!

開発部の案内

トリドリのプロダクト開発部では通年採用を実施しています!

ユーザの幸せについて真剣に議論したり、「やってみたい」で新しい技術に挑戦をしてみたり、気づいたら30分雑談していたり、ゆるく真面目に開発しています。もし興味を持ってもらえたら、こちらを読んでみてください!

https://toridori.notion.site/toridori-7e9c804bcfc249d6a932cdb99b83e1f6

トリドリバコ

Discussion