VBAをTDDしよう。
Excel VBAのテスト自動化。
VBAをテストしたければ、VBAでテストを書くしかない。 しかし、それはExcelを開いてマクロを手動やボタンで実行することになる。 そこには以下の問題がある
-
Excelをいちいち開く必要
-
テスト対象とテストの分離が困難(同時に開くことが必須)
-
Excel自体がバージョン管理できない(vbacはあるが)
テスト対象はどうしようもないが、テストの独立性確保とバージョン管理はしておきたい。 それならPythonでテストを書ければいいのではないか。
Excel VBAのユニットテストをPythonで書いて、自動実行する。
Pythonなら使えるライブラリが多く、Excelと組み合わせて紹介されることも多い。 Excel自動化をキッカケにPythonを覚えた人も多いはず。
Pythonにはxlwingsという便利な物があるので、COMを直接操作するより遙かに楽で、ツール自体の品質管理もやりやすい。
テストスイートの定義と選択をコマンドで行いたい。 予めテストスイートを定義しておいて、その名前を与えることで選択できるようにする。
ツールにはテストケースの定義を指定する必要があり、テストケースの集合はテストシナリオファイルにまとめる。 テストシナリオとテストスイートは密接に関わっているため、テスト設計者は両方の作成を担当しているはず。 それなら同じconfigファイルに両方書いてもらうのが一番間違いない。
PythonからExcelを操作できると言っても、VBAを自由に使えるわけではない。
Application.Run()
で実行できるとはいえ、以下の問題がある。
-
Pythonで生成できないオブジェクトが引数にある
-
ByRef引数が戻ってこない
-
フォームモジュールに触れない
VBA上でしかできないことなら、VBAでさせればいいというわけで、Bridgeブックを用意した。
テスト対象は動的にBrigeの参照設定に追加して、Bridge経由で全ての公開メンバにアクセス可能にする。
CallByName()
を使えばByRefの値も取れるので、任意の型の値をPythonでチェックできる。
Bridge経由の汎用的な通信では実現し得ない、フォームオブジェクトの操作やクラスモジュールのインスタンス化などもテストとしては必要になる。 これはCOMでどうこうできないので、VBAのコードを動かすしかない。 テストコードを配布物に含めたくないから、テスト時に動的に追加したい。 ツールとしてはこの操作を実現する。
Excelにまつわるテストなのだから、分かりやすくExcelで作ってもらう。 このテストシナリオファイルをconfigで指定する。
テスト実行時、特に指定しなければこのファイルがある場所をカレントディレクトリにする。 テストコードのパスはカレントディレクトリからの相対パスだから、基本構成はこうなる。
-
root
-
シナリオ.xlsx
-
config.json
-
GroupA
-
a1_test.py
-
a2_test.py
-
-
GroupB
-
b1_test.py
-
b2_test.py
-
-
開発も考えるとこうなるはず。
-
root
-
.git
-
.env
-
.gitignore
-
product-test.code-workspace
-
シナリオ.xlsx
-
その他
-
beforeEachとafterEachみたいなものを持たせない。 これはテストスイートに対して定義されるはずで、上記のconfigの話になる。 しかし厳密に言えばテストケース毎に定義されるものであり、同じ準備をして同じ始末をするテストケース群が一個のグループなのだと思う。