VBAツールの品質を担保するためにPythonで検証基盤を作った話
0. はじめに
仕事をしていると、効率化のためにVBAでツール化することが度々ありましたが、そのたびに「このツール、どこかバグっていないかな?」という不安が付きまといました。
AIにツール作成を頼むこともありますが、意図が正しく伝わっていなかったり、AIに任せたことでコードのチェックがおろそかになってしまうこともあります。さらに、コードが膨大になるとそもそも追い切れず、バグが混入しても気づかないケースもあります。
そして一番怖いのは、ツールにバグがあることに気づかず、その結果を正しいものとして仕事が進んでしまうことです。後になって「これはツールのバグだった」と分かると、それまでの作業がすべて無駄になってしまう可能性もあります。
今回、個人的にVBAツールを作成したことをきっかけに、「ツールの信頼性をどう担保するか」を考え、検証の仕組みを初めて構築してみることにしました。
本記事では、VBAツールの信頼性を担保するために、Pythonを用いた検証基盤の構築方法を紹介します。
また、本記事で紹介する検証基盤はGitHubで公開しており、実際の構成やコードも確認できるようにしています。
1. 今回の対象ツール
検証対象としたVBAツールの目的は、検証ログやセンサログの解析作業を効率化することです。
また、VBAを選択した理由は、どのような環境でも利用できる可能性が高く、業務への適用範囲が広いためです。
このツールでは、設定シートを変更するだけで以下の処理を自動化できるようにしています。
- 必要な信号の抽出
- 単位変換の適用
- 条件付きデータ抽出
- Excelグラフによる可視化
詳細は下記記事をご覧ください。
Excel VBAでCSVログ解析ツールを作ったので公開します - AI Fusion Design
2. 課題:手動検証の限界
ツールを作成した後、手動で軽く確認することはできますが、機能が増えるほど確認すべき項目も増えていきます。
さらに、複数の機能を同時に有効にした場合でも正常に動作するのかを考え始めると、検証パターンは一気に増えていきます。
これらを一つ一つ手動で設定し、結果を確認していくとなると工数は膨大になり、効率化のためにツールを作ったにもかかわらず、検証に時間がかかってしまうという本末転倒な状態になります。
また、人の目視確認だけでは見逃しのリスクもあり、「このツールは正しい」と自信を持って言える状態にはなりません。
3. 解決方針
そこで、以下の検証フローを構築することにしました。
- 検証項目を事前に定義する
- VBAツールを自動実行する
- Pythonで期待値を生成する
- 両者を比較する
このように「別の手段で正解を作り、それと比較する」ことで、VBA側の実装ミスによるバグを効率的に検出できると考えました。
Pythonを選択した理由としては、データ処理に適した標準ライブラリが充実しており、VBAに比べてシンプルに記述できるためです。その結果、期待値生成側のバグ混入リスクを抑えつつ、開発時間の短縮と信頼性向上の両立が可能になります。
(ただし、両方に同じバグが入る可能性もあるため、目視確認を完全に不要にできるわけではないと考えます)
4. 検証設計(テストケース設計)
今回の検証では、ツールの各機能を個別に確認するだけでなく、「機能の組み合わせ」まで含めて網羅的に確認することを目的としました。
検証対象の主な機能は以下の3つです。
- 信号変換
- 時刻範囲抽出
- 行フィルタ
これらに対して、以下の観点でテストケースを設計しました。
■ 単機能の検証
まずは各機能単体での動作確認を行いました。
- 基本抽出(T01)
- 信号変換ON/OFF(T02)
- 時刻範囲ON/OFF(T03)
- 行フィルタON/OFF(T04)
さらに、信号変換については以下のようにパターンを細分化しています。
- identity(変換なし)
- 定数出力
- 特定信号のみ変換
- 全信号変換
これにより、「変換ロジックそのもの」に問題がないことを確認しています。
■ 境界条件の検証
実務でバグが発生しやすいポイントとして、境界条件の検証も重点的に行いました。
- 単一点抽出(T09)
- 境界値を含む範囲(T10)
- 全範囲指定(T11)
特に、境界値を含むかどうかは仕様によって結果が変わるため、意図通りの挙動になっているかを確認しています。
■ 条件分岐(行フィルタ)の検証
行フィルタについては、すべての比較パターンを網羅しています。
- == / != / > / < / <=
- 許容誤差あり(tol)条件
これにより、単純な比較だけでなく、実務で頻出する「誤差を含めた判定」についても確認しています。
■ 複合条件の検証
単機能だけでなく、複数機能を同時に使用した場合の挙動も確認しました。
- 信号変換 + 時刻範囲(T19)
- 信号変換 + 行フィルタ(T20)
- 時刻範囲 + 行フィルタ(T21)
- 信号変換 + 時刻範囲 + 行フィルタ(T22)
これにより、「個別では正常だが、組み合わせると崩れる」といった不具合を検出できるようにしています。
■ テストケース一覧
ここまでの内容を整理すると、検証項目は下表のようになります。 テストケースは内部的には設定パターンで管理していますが、記事では検証観点ごとに整理して示します。
| テストID | 検証内容 | 信号変換 | 時刻範囲 | 行フィルタ |
|---|---|---|---|---|
| T01 | 基本抽出 | OFF | OFF | OFF |
| T02 | 信号変換ON | ON(SigA) | OFF | OFF |
| T03 | 時刻範囲ON | OFF | ON | OFF |
| T04 | 行フィルタON | OFF | OFF | ON |
| T05 | identity変換(実質的には値を変えない) | ON(1倍に設定) | OFF | OFF |
| T06 | 定数変換 | ON(固定値に変換) | OFF | OFF |
| T07 | SigBのみ変換 | 一部ON | OFF | OFF |
| T08 | 全信号変換 | 全てON | OFF | OFF |
| T09 | 単一点抽出 | OFF | 単一点 | OFF |
| T10 | 境界含む範囲 | OFF | 境界含む | OFF |
| T11 | 全範囲 | OFF | 全範囲 | OFF |
| T12 | フィルタ == | OFF | OFF | == |
| T13 | フィルタ != | OFF | OFF | != |
| T14 | フィルタ > | OFF | OFF | > |
| T15 | フィルタ < | OFF | OFF | < |
| T16 | フィルタ <= | OFF | OFF | <= |
| T17 | フィルタ ==(誤差あり) | OFF | OFF | ==(tol) |
| T18 | フィルタ !=(誤差あり) | OFF | OFF | !=(tol) |
| T19 | 変換 + 時刻 | ON | ON | OFF |
| T20 | 変換 + フィルタ | ON | OFF | ON |
| T21 | 時刻 + フィルタ | OFF | ON | ON |
| T22 | 全機能ON | ON | ON | ON |
■ テスト設計の考え方
今回のテストでは、すべてのパターンを網羅するのではなく、以下の方針で設計しています。
- 各機能の代表的なパターンを押さえる
- バグが発生しやすい境界条件を重点的に確認する
- 機能の組み合わせによる影響を確認する
このように、「最小限のケースで最大限の不具合を検出できる構成」を意識しました。
■ 今回の範囲で十分と判断した理由
検証ケースはT01〜T22の22ケースとしていますが、以下の理由から今回の範囲で十分と判断しました。
- 各機能のON/OFFおよび代表パターンを網羅している
- 境界条件(single / boundary / all)をカバーしている
- すべての比較演算子を確認している
- 単機能・複合機能の両方を検証している
これにより、「ロジックの破綻」「条件分岐ミス」「組み合わせバグ」といった主要な不具合パターンは検出可能と判断しています。
5. 実装
今回構築した仕組みを一言で表すと、「VBAで実測を自動生成し、Pythonで期待値を生成・比較し、その結果を summary に反映する検証パイプライン」です。
全体の実行は、VBA側の RunAllTargetCases という関数が担っています。
この関数は、検証をボタン1つでまとめて実行するためのオーケストレータとして動作します。
実行の流れは以下の通りです。
RunAllTargetCases
-> 1. ケース一覧を走査(実行対象=ON)
-> RunOneCaseFromRow
-> VBAツールへ設定反映
-> RunPipeline 実行
-> 出力保存
-> COL_VBA_RESULT を更新
-> RunPythonCompareAll(設定があれば)
-> Python実行(--book のみ、全件比較)
-> summary.csv を生成
-> ApplyPythonSummaryToSheet
-> COL_PY_RESULT / COL_COMPARE / COL_ERROR を反映
まず、検証管理ブックの 1. ケース一覧 シートから、実行対象=ON になっているテストケースを順に取得します。
各ケースについては RunOneCaseFromRow が呼ばれ、実行用のVBAツールへ設定内容を反映します。
設定反映後に RunPipeline を実行すると、VBA側で指定どおりの出力Excelが生成されます。
この処理を対象ケース分だけ繰り返すことで、各テストケースの実測結果をまとめて作成できます。
その後、VBA側の出力生成がすべて完了したら、RunAllTargetCases からPythonを起動し、全ケースの比較処理をまとめて実行します。
Python側では比較結果を summary.csv として出力し、最後に ApplyPythonSummaryToSheet がその内容を 1. ケース一覧 シートへ反映します。
これにより、各テストケースについて
- VBA実行結果
- Python比較結果
- 最終的なOK/NG判定
- エラー内容
を一覧で確認できるようになりました。
この仕組みの要点は、テストケース定義をExcelに集約し、実測生成はVBA、期待値生成と比較判定はPythonに分離したことです。
これにより、検証のたびに手作業で結果を確認する必要がなくなり、ツールを修正した後でも「変更によって意図しない不具合が入っていないか」を短時間で確認できるようになりました。 また、期待値生成と比較処理をPython側に分離することで、VBA本体の修正と検証ロジックを独立して扱えるようになり、メンテナンス性の面でも有利な構成としています。
6. 不具合と修正
検証を回していく中で、主に以下の3つの不具合に遭遇しました。
- VBAツールの実行が止まってしまう不具合
- 期待値(Python)と異なる出力値になっていた不具合
- 期待値(Python)とは一致するが、想定と異なる出力になっていた不具合
■ 1. VBAツールの実行が止まる不具合
信号変換をONにし、かつ行フィルタ(条件分岐)もONにした場合、
元信号名を指定するとVBAツール側で「信号が存在しない」と誤認識し、処理が停止してしまう不具合がありました。
この不具合は、単機能では発生せず、機能の組み合わせ(複合条件)をテストしたことで初めて顕在化したものです。
今回、複合条件を検証項目として含めていたことで発見することができました。
■ 2. 期待値(Python)と不一致となった不具合
行フィルタにおける「!=(不一致)」条件について、VBA側では許容誤差(tol)が適用されていない一方で、Python側では適用されていました。
その結果、両者の出力に差分が発生し、不具合として検出されました。
これは、Python側で期待値を別ロジックとして生成していたことで初めて発見できた不具合です。
もしVBA単体で検証していた場合、見逃されていた可能性があります。
■ 3. 一致しているが仕様として誤っていたケース
信号変換と行フィルタを同時にONにした場合、
- 変換後の値でフィルタするのか
- 元の値でフィルタするのか
という仕様が明確に定義されていませんでした。
その結果、VBAとPythonの両方が「元の値でフィルタする」実装になっており、出力結果は一致していましたが、仕様としては意図しない挙動になっていました。
このケースでは、
- 仕様を再検討し、「変換後の値でフィルタする」と定義し直す
- VBAツールを修正する
- 修正後に一度不一致を確認する
- Python側の期待値も修正し、最終的に一致させる
という手順を踏むことで、仕様と実装の両方を正しい状態に揃えました。
これは、検証パターンを作ったことで初めて仕様の曖昧さに気づけたケースであり、単なるバグ検出にとどまらず、仕様の明確化にもつながった重要な発見でした。
■ 最終結果
最終的には、すべてのテストケースにおいて期待値と一致する状態まで整えることができました。
今回の検証を通じて、単に「ツールが動く」状態から、「仕様通りに正しく動いていると確認できる状態」へと一段階完成度を高めることができました。
また、検証基盤を導入したことで、「結果が合っているか」だけでなく、「仕様が正しいか」まで確認できるようになりました。
7. 完成した検証フロー
今回構築した検証フローは、以下のような流れになります。
- Excelでテストケースを定義する
- VBAで各ケースの出力(実測)を自動生成する
- Pythonで期待値を生成する
- 両者を比較し、差分を判定する
- 結果をExcelに一覧として反映する
この一連の流れをボタン1つで実行できるようにしたことで、検証作業をほぼ自動化することができました。
また、テストケースをExcelで管理しているため、ケースの追加や変更も容易であり、ツールの修正後に再検証を行うハードルも大きく下がりました。
8. 得られた価値
今回の検証基盤構築によって、単なるツール開発から一歩進み、品質の担保という観点で大きな価値を得ることができました。
主なポイントは以下の通りです。
- 検証の自動化 手動確認に頼らず、短時間で全ケースの確認が可能になった
- バグ検出力の向上 単機能だけでなく、複合条件による不具合も検出できるようになった
- 仕様の明確化 一致しているが意図と異なるケースを通じて、仕様そのものを見直すことができた
- 再利用可能な検証基盤 今回の仕組みは他のツールにも応用可能な形になっている
特に、「結果が合っているか」だけでなく、「仕様として正しいか」まで確認できるようになった点は大きな変化でした。
また、今回構築した検証基盤はGitHubで公開していますので、実際の構成やコードも確認いただけます。
csv-log-extractor-vba/validation at main · itubo224-gif/csv-log-extractor-vba
9. 今後の展開
今回構築した検証基盤は、まだ改善の余地もあります。
例えば以下のような点は今後の課題と考えています。
- 異常系(不正入力やデータ欠損)の検証追加
- テストケースのさらなる体系化
- Python側の処理の汎用化・モジュール化
また、今回の仕組みは「ツールを作る → 検証する」という流れですが、将来的には
- AIによるロジック生成
- 自動で検証ケースを生成
- 自動で検証・修正を回す
といった形で、設計・実装・検証を一体化した開発フローへと発展させていきたいと考えています。
今回の取り組みを通じて、「ツールを作る」だけでなく、「品質を担保する仕組みまで設計する」ことの重要性を強く実感しました。
