この記事は Qiita Tech Festa 2026 の投稿記事かつ 負債化しないシェルスクリプト設計:Bashに「層」を設けてテスト容易性と保守性を手に入れる の続編です。
はじめに
前回の記事では、Bashスクリプトに「Functional Core, Imperative Shell(FCIS)」のアーキテクチャを取り入れ、bats-core を使ってテスト可能な状態にする手法を解説しました。
今回はその続編として、テストを CI(GitHub Actions)で自動化し、kcov を用いてカバレッジ率を計測する 方法をご紹介します。
また、ローカル環境とCI環境でのツール群のバージョンやコマンド差異を防ぐため、最近人気を集めているツールマネージャー mise を用いて環境構築とタスク実行を統一します。
概要・前提条件
本記事では以下のツールを使用します。
- bats-core : Bash用のテストフレームワーク
- kcov : コンパイル言語だけでなく Bashスクリプトの網羅率(カバレッジ)も計測できるツール
- mise : でプロジェクトのツール群とタスクを一元管理するマネージャー
本記事では、kcov がローカル環境にインストールされていることを前提としています。
kcov は Ubuntu 24.04 などでは OS のパッケージマネージャーで配布されなくなっているので、公式のインストール手順 を参考にビルドするか、公式提供の Docker image を利用することになります
本題・実装
1. サンプルコードの準備
まずはカバレッジの「通っている行」「通っていない行」を可視化するための簡単なサンプルを用意します。
足し算と引き算の関数を持つ math.sh と、足し算だけをテストする test_math.bats を作成します。
#!/usr/bin/env bash
add() {
echo $(($1 + $2))
}
subtract() {
# この関数はテストから呼ばれないため、カバレッジ上は「未実行」になる
echo $(($1 - $2))
}
#!/usr/bin/env bats
setup() {
source ./math.sh
}
@test "add function should return sum" {
run add 2 3
[ "$status" -eq 0 ]
[ "$output" = "5" ]
}
2. miseを使ったローカル環境とタスクの構築
今回は bats-core をツールマネージャー mise で管理し、テスト実行コマンドを mise のタスクとして定義します。
カバレッジ取得には、ローカル環境にインストールされた kcov を使用します。
以下の内容でプロジェクトルートに mise.toml を作成します。
[tools]
"aqua:bats-core/bats-core" = "1.11.0"
[tasks.test]
description = "Run bats tests"
run = "bats ./test_math.bats"
[tasks.coverage]
description = "Run tests and generate kcov coverage report"
run = "kcov --include-pattern=.sh coverage/ bats ./test_math.bats"
設定ができたら、mise install を実行してツール群をインストールします。
$ mise install
これにより、mise.toml に定義したコマンドを mise run 経由で呼び出せるようになります。
$ mise run coverage
# (実行結果例)
# kcov がローカルで bats を実行し、coverage/ ディレクトリにレポートを生成します
# ✓ add function should return sum
#
# 1 test, 0 failures
実行後、coverage/ ディレクトリ内にHTML形式のレポートが出力されます。
キャプチャを見ると、add 関数の行が緑色(実行済み)、subtract 関数の行が赤色(未実行)になっていることが確認できるはずです。
3. GitHub Actions ワークフローの実装
ローカルで動くようになったら、これを GitHub Actions に組み込みます。
CI上でも公式アクション jdx/mise-action を利用することで、ツールのセットアップからタスクの実行までを非常にシンプルに記述できます。
さらに、CI環境ではテスト結果を見やすくするため、bats の出力を JUnitフォーマット に変換するのもおすすめです。
今回はテスト結果(JUnit XML)を出力するタスク ci-test を追加して、結果をGitHub Actions上で可視化する構成にします。(※CI環境でのカバレッジ取得については後述の課題があるため、今回はテストの実行のみを扱います)
mise.toml にCI用のタスクを追記します。
[tasks.ci-test]
description = "Run tests and output JUnit XML"
# batsの --formatter junit を使い、結果を report.xml に書き出します
run = '''
rm -f report.xml
bats --formatter junit ./test_math.bats > report.xml
'''
これを GitHub Actions のワークフローで呼び出します。
※テスト用なので GitHub Actions Workflow のセキュリティ面は厳密に考慮した記述になっていません。例えばActions をバージョンタグで呼び出していますが、実運用では suzuki-shunsuke/pinact などを利用して SHA1 Hash で指定バージョンを固定してください
name: Bash Tests
on:
push:
branches: [ "main" ]
pull_request:
permissions:
contents: read
checks: write
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup tools with mise
uses: jdx/mise-action@v4
- name: Run CI test task
run: mise run ci-test
# JUnitレポートを解釈してPR等にテスト結果を表示するアクション
- name: Publish Test Report
uses: mikepenz/action-junit-report@v6
if: success() || failure()
with:
report_paths: 'report.xml'
include_passed: 'true'
ポイント:
-
jdx/mise-actionを使うことで、ローカルと同じインストール手順でインストールができます -
bats --formatter junitを指定してreport.xmlを出力し、mikepenz/action-junit-report(または同等のアクション)に渡すことで、GitHub上で「何ケース成功して何ケース失敗したか」の数値をグラフィカルに確認できるようになります
4. テスト結果の確認
GitHub Actionsの実行が完了すると
Pull Request の Check の Bash Tests / JUnit Test Report (pull_request) の項目にテストの失敗・成功
ワークフローの「Summary」ページにテストの成功・失敗の件数や詳細がグラフィカルに表示されます。
ローカルでは mise run coverage でカバレッジを確認しつつ、CIではプルリクのたびに自動テストが走ってコードの健全性を担保する、というワークフローがこれで完成です。
おわりに & CI 環境でのカバレッジ取得の課題
前回の「FCISなアーキテクチャによるテスト容易性の確保」に続き、本記事では 「mise を使った環境・タスクの統一」 を行い、ローカルでの bats テストと kcov によるカバレッジ取得環境を整備しました。
一方で、このカバレッジ計測を GitHub Actions 上で実践しようとすると、kcov のインストールが意外と厳しい という課題があります。
OSの公式パッケージを apt-get でインストールしようとするとバージョンが古かったり、逆にソースから都度ビルドするとCIの実行時間が延びてしまう等の悩ましさがあります。
手軽に導入できそうな第三者のアクション(例えば action-kcov )も存在しますが、中身を見ると2020年に作られたコードに latest (07/2024) などの新しいタグだけが付けられて更新されているような状態で、プロダクションでの積極的な利用はおすすめしづらいと感じました。
そのため、本記事の GitHub Actions ワークフローでは kcov の実行を見送り純粋なテストのみの構成としましたが、今後は「CI上でもカバレッジを取得できる方法」を引き続き探していきたいと考えています。


