1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

テストケースを書いたExcelもGitで管理する

Last updated at Posted at 2025-12-14

はじめに

以前書いたExcel、Excel VBA をGitで管理するという記事が予想以上に反響があったので、テストケースを書いたExcelもGitで管理できないか考えてみたので、記事にしてみました。

システム開発において、すべてのテストをコードで管理するのは難しいのが現状です。
そのため、一部のテストにおいてはExcelやConfluenceで管理されていらっしゃる方も多いのではないでしょうか?

単体テストやE2Eテストなどはコードで管理しているのに、それらを除く一部のテストは別の媒体で管理する。。。なんてのは可能な限り避けたいですよね。

というわけで、テストケースが書かれたExcelもGitで管理できないかを考えてみました。

テストケースを書いたExcelをGitで管理する

ExcelをGitで管理すると、結局バイナリ管理になります。

そこで、前回と同じくpre-commitのライブラリを使用して、コミット前にExcelからテストケースをMarkdownへと抽出するようなロジックを考えました。

なお、Excelからテストケースを抽出するのはpandasopenpyxlのライブラリを使用します。

# .pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      - id: extract-testcases
        name: Extract Testcases from Excel files
        entry: uv run python .githooks/extract_testcases.py
        language: system
        files: '\.xlsx$'
        pass_filenames: false
        always_run: false
        stages: [pre-commit]

以下、コミット時に発火するソースコード。

# .githooks/extract_testcases.py
import sys
import os
import subprocess
import pandas as pd

TARGET_EXCEL_FILE = ('.xlsx')
TARGET_DIRS = [
    'content/docs/50-dev-docs/tests'
]
IGNORE_COLUMNS = [
    '結果',
    '実施日',
    '実施者',
    '補足'
]

def is_target_excel(path):
    """
    パスが対象ディレクトリのいずれかの配下にあるExcelファイルかどうかを判定する。
    """
    if not path.lower().endswith(TARGET_EXCEL_FILE):
        return False
    normalized = os.path.normpath(path)
    for target_dir in TARGET_DIRS:
        normalized_target = os.path.normpath(target_dir)
        if normalized.startswith(normalized_target + os.sep) or normalized == normalized_target:
            return True
    return False

def find_staged_excels():
    """
    git のステージに乗っているファイルのうち、
    対象ディレクトリ内の拡張子 .xlsx を持つものを返す。
    """
    # --diff-filter=ACM: Added, Copied, Modified
    # core.quotePath=false で日本語ファイル名を正しく処理
    result = subprocess.run(
        ['git', '-c', 'core.quotePath=false', 'diff', '--cached', '--name-only', '--diff-filter=ACM'],
        capture_output=True,
        text=True,
        encoding='utf-8'
    )
    if result.returncode != 0:
        print("Error: git diff failed", file=sys.stderr)
        sys.exit(1)
    files = result.stdout.strip().split('\n')
    if not files or files == ['']:
        return []
    
    # 対象ディレクトリ内のExcelファイルのみをフィルタリング
    excels = [f for f in files if is_target_excel(f)]
    print("Found testcases: ", excels)
    
    return excels

def convert_excel_to_markdown(excel_path):
    """
    excel_path を読み込み、同じ名前の .md を生成。
    Excelの先頭シートをMarkdown表形式で出力する。
    """
    base, _ = os.path.splitext(excel_path)
    md_path = base + '.md'
    print(f"Converting '{excel_path}''{md_path}'")

    try:
        # Excel の読み込み(先頭シート)
        df = pd.read_excel(excel_path, sheet_name=0)
        
        # 不要なカラムを削除(存在しない場合はエラーにしない)
        df = df.drop(columns=IGNORE_COLUMNS, errors='ignore')
        
        # Markdown表形式に変換
        headers = [str(c) for c in df.columns]
        rows = []
        for _, row in df.iterrows():
            # セル内の改行を<br>に置換
            rows.append(["" if pd.isna(v) else str(v).replace('\r\n', '<br>').replace('\n', '<br>').replace('\r', '<br>') for v in row])
        
        # Markdown表の行を構築
        lines = []
        # ヘッダー行
        lines.append("|" + "|".join(headers) + "|")
        # 区切り行
        lines.append("|" + "|".join(["---"] * len(headers)) + "|")
        # データ行
        for row in rows:
            lines.append("|" + "|".join(row) + "|")
        
        # ファイルに書き出し
        content = "\n".join(lines) + "\n"
        with open(md_path, 'w', encoding='utf-8') as f:
            f.write(content)
        
        subprocess.run(['git', 'add', md_path], check=False)

    except Exception as e:
        print(f"Failed to convert '{excel_path}': {e}", file=sys.stderr)
        sys.exit(1)

def main():
    excels = find_staged_excels()
    if not excels:
        sys.exit(0)

    for excel in excels:
        convert_excel_to_markdown(excel)

    sys.exit(0)

if __name__ == '__main__':
    main()

これにより、ExcelからテストケースをMarkdwonで抽出することができました。
抽出したMarkdownを他のドキュメントと合わせてWeb上で参照できるようにすることで、わざわざExcelファイルを開くことなくテストケースの内容を確認できます。

また、テストケースの変更履歴やプルリクを使ったレビューもできるので、良いとは思いませんか?

スクリーン ショット 2025-12-15 に 00.09.14 午前.png

1点気をつけるべきポイントとしては、あくまでExcelをメインで管理することは変わらないことです。
Markdownのファイルを直接編集しないように注意しましょう。

さいごに

今回、このような仕組みを作ったのは、たまたまテストケースの書かれたExcelをGitで管理しているチームがいたのですが、レビュー時に「テストケースの変更箇所の行番号をPRコメントで大量に残していた」「レビュアーがわざわざExcelファイルを開いて確認していた」のを見て、「手間だなぁ。。。」と思ったからです。

テストケースに限らず、色々なところで応用ができる手段だと思うので、是非みなさん色々と試してみてください。

👋👋👋

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?