ねらい:Excelで「品質保証(テスト)」を仕組みにして、拡張しても壊れないようにする
手作業の確認は限界があります。Excel VBAでも「テストの型」を最初に入れておくと、機能追加や修正のたびに自動検証でき、安心して前進できます。ここでは、初心者でも貼って動かせるテスト枠(ランナー、アサーション、フィクスチャ、ゴールデンマスター、依存の差し替え)を示し、検証・集計・ソートの例題まで通します。重要なのは、配列I/Oでテストを速く回し、同じ入力に同じ出力が出る「再現性」を担保することです。
テストの基本設計:小さいユニットテストを積み重ねる
テストは「ユニット→統合→エンドツーエンド」の順で小さいものを積み重ねます。まずは単機能(検証、集計、ソートなど)を、入力と期待値のペアで確かめるユニットテストから始めます。外部I/O(ファイル、ネット、時刻)は結果が揺れやすいので、関数化してテスト中だけ差し替えます。これにより「同じテストは毎回同じ結果」になり、信頼性が跳ね上がります。
重要ポイントの深掘り
ユニットテストは「速く・頻繁に」回せるのが価値です。配列を渡して配列で返す関数にしておくと、シート往復がなく高速で、期待値比較も容易です。揺らぎ要因(現在時刻、乱数、環境差)はすべて関数経由にし、テスト中は固定値に切り替えます。
アサーションとテストランナーのテンプレート
テストで使う「判定(アサーション)」と「結果をまとめて出すランナー」を用意します。プロシージャ名を登録し、順に実行して合否とメッセージをシートへ出力します。
アサーションの最小セット
' ModAssert.bas
Option Explicit
Public Passed As Long, Failed As Long, CurrentCase As String
Public Sub StartCase(ByVal name As String)
CurrentCase = name
End Sub
Public Sub EndCase()
CurrentCase = ""
End Sub
Public Sub AssertTrue(ByVal cond As Boolean, ByVal msg As String)
If cond Then
Passed = Passed + 1
Else
Failed = Failed + 1
Debug.Print "FAIL: "; CurrentCase; " - "; msg
End If
End Sub
Public Sub AssertEquals(ByVal expected As Variant, ByVal actual As Variant, ByVal msg As String)
Dim ok As Boolean
ok = (CStr(expected) = CStr(actual))
AssertTrue ok, msg & " (expected=" & CStr(expected) & ", actual=" & CStr(actual) & ")"
End Sub
Public Sub AssertArray2DEquals(ByRef exp As Variant, ByRef act As Variant, ByVal msg As String)
Dim ok As Boolean: ok = True
If UBound(exp, 1) <> UBound(act, 1) Or UBound(exp, 2) <> UBound(act, 2) Then ok = False
Dim r As Long, c As Long
If ok Then
For r = 1 To UBound(exp, 1)
For c = 1 To UBound(exp, 2)
If CStr(exp(r, c)) <> CStr(act(r, c)) Then
ok = False
Exit For
End If
Next
If Not ok Then Exit For
Next
End If
AssertTrue ok, msg
End Sub
VBテストランナーと結果出力
' ModTestRunner.bas
Option Explicit
Private tests As Collection
Public Sub Register(ByVal procName As String)
If tests Is Nothing Then Set tests = New Collection
tests.Add procName
End Sub
Public Sub RunAllTests()
Passed = 0: Failed = 0
If tests Is Nothing Or tests.Count = 0 Then
MsgBox "テストが登録されていません。", vbExclamation
Exit Sub
End If
Dim ws As Worksheet: Set ws = PrepareOut("Tests")
ws.Range("A1:D1").Value = Array("Case", "Result", "Message", "Time")
Dim i As Long, rowOut As Long: rowOut = 2
For i = 1 To tests.Count
Dim name As String: name = CStr(tests(i))
StartCase name
Dim t0 As Double: t0 = Timer
On Error Resume Next
Application.Run tests(i)
Dim errDesc As String: errDesc = Err.Description
On Error GoTo 0
EndCase
Dim res As String: res = IIf(Len(errDesc) = 0, "OK", "ERROR")
ws.Cells(rowOut, "A").Value = name
ws.Cells(rowOut, "B").Value = res
ws.Cells(rowOut, "C").Value = errDesc
ws.Cells(rowOut, "D").Value = Format(Timer - t0, "0.000")
rowOut = rowOut + 1
Next
ws.Cells(rowOut, "A").Value = "Passed": ws.Cells(rowOut, "B").Value = Passed
ws.Cells(rowOut + 1, "A").Value = "Failed": ws.Cells(rowOut + 1, "B").Value = Failed
ws.Columns.AutoFit
MsgBox "テスト完了: Passed=" & Passed & " Failed=" & Failed, vbInformation
End Sub
Private Function PrepareOut(ByVal name As String) As Worksheet
Dim ws As Worksheet
On Error Resume Next: Set ws = Worksheets(name): On Error GoTo 0
If ws Is Nothing Then Set ws = Worksheets.Add: ws.Name = name
ws.Cells.Clear
Set PrepareOut = ws
End Function
VB重要ポイントの深掘り
VBAは関数ポインタを直接渡せないため、プロシージャ名を文字列で登録し Application.Run で呼ぶのがシンプルです。アサーションは「失敗時だけ詳しく」出すと、ログが読みやすくなります。シートに「Case/Result/Message/Time」を残すことで、後から原因分析が容易です。
フィクスチャ(固定データ)と再現性の確保
テストごとに「小さく・明確な」入力配列を作るフィクスチャを用意します。乱数や時刻などの揺らぎはコントロールし、毎回同じ結果になるようにします。
フィクスチャ生成と揺らぎ制御
' ModFixture.bas
Option Explicit
Public Function SmallTable() As Variant
Dim a() As Variant: ReDim a(1 To 6, 1 To 3)
a(1, 1) = "Key": a(1, 2) = "Name": a(1, 3) = "Amount"
a(2, 1) = "b": a(2, 2) = "Baker": a(2, 3) = "10"
a(3, 1) = "a": a(3, 2) = "Able": a(3, 3) = "20"
a(4, 1) = "a": a(4, 2) = "Able": a(4, 3) = "20"
a(5, 1) = "c": a(5, 2) = "Charlie": a(5, 3) = "5"
a(6, 1) = "b": a(6, 2) = "Baker": a(6, 3) = "7"
SmallTable = a
End Function
Public Sub SeedRandom(Optional ByVal seed As Long = 42)
Randomize seed
End Sub
Public Function NowClock() As Date
NowClock = #1/1/2025 12:00:00 AM# ' テスト時は固定時刻
End Function
VB重要ポイントの深掘り
テストで乱数や時刻を使う場合は、必ずテスト用のラッパー関数に寄せて固定値にします。外部ファイルを読むテストは、テンポラリフォルダを作って小さなCSVを用意し、実行後に必ず削除します。これで「他の環境でも同じように通る」テストになります。
例題テスト:検証・集計・ソートのユニットテスト
単機能を確かめる three-in-one の例を示します。検証は必須・型チェック、集計はキーごとに合計、ソートは安定ソートで元順序保持を確認します。
検証のテスト
' Test_Validate.bas
Option Explicit
Public Sub Test_Validate_RequiredAndType()
Dim a As Variant: a = SmallTable()
Dim errs As New Collection
Dim r As Long
For r = 2 To UBound(a, 1)
If Len(Trim$(CStr(a(r, 1)))) = 0 Then errs.Add "Key empty at " & r
If Not IsNumeric(a(r, 3)) Then errs.Add "Amount not numeric at " & r
Next
StartCase "Validate Required/Type"
AssertEquals 0, errs.Count, "エラーは0件のはず"
EndCase
End Sub
VB集計のテスト
' Test_GroupSum.bas
Option Explicit
Public Sub Test_GroupSum_ByKey()
Dim a As Variant: a = SmallTable()
Dim d As Object: Set d = CreateObject("Scripting.Dictionary")
Dim r As Long
For r = 2 To UBound(a, 1)
Dim k As String: k = LCase$(Trim$(CStr(a(r, 1))))
Dim v As Double: v = Val(CStr(a(r, 3)))
d(k) = IIf(d.Exists(k), d(k) + v, v)
Next
StartCase "GroupSum by Key"
AssertEquals 17, d("b"), "bの合計は10+7=17"
AssertEquals 40, d("a"), "aの合計は20+20=40"
AssertEquals 5, d("c"), "cの合計は5"
EndCase
End Sub
VBソートのテスト(安定性確認)
' Test_SortStable.bas
Option Explicit
Public Sub Test_SortStable_ByName()
Dim a As Variant: a = SmallTable()
Call StableSort2D(a, 2, True) ' 2列目Nameで昇順(安定)
StartCase "StableSort by Name"
AssertEquals "Able", a(2, 2), "先頭データはAble"
AssertEquals "Able", a(3, 2), "次もAble(重複保持)"
AssertEquals "Baker", a(4, 2), "その次はBaker"
EndCase
End Sub
VB重要ポイントの深掘り
期待値は「なぜその値になるか」が説明できる小さなデータで作ります。安定ソートは「同値の元順序が保たれているか」を確認すると、現場で起きやすいタイブレーク事故を防げます。集計は文字と数値が混在しがちなので、ValやCDblで数値化して比較します。
ゴールデンマスター(期待出力の保存)と差分テスト
「複数ステップの処理を通した最終出力」が大きい場合、期待出力(ゴールデン)をCSVやシートに保存しておき、毎回の出力とハッシュや行差分で比較します。
ゴールデンの保存と比較
' ModGolden.bas
Option Explicit
Public Sub SaveGolden(ByVal wsName As String, ByVal goldenSheet As String)
Dim ws As Worksheet: Set ws = Worksheets(wsName)
Dim a As Variant: a = ws.Range("A1").CurrentRegion.Value
Dim g As Worksheet: Set g = PrepareOut(goldenSheet)
g.Range("A1").Resize(UBound(a, 1), UBound(a, 2)).Value = a
End Sub
Public Sub CompareWithGolden(ByVal wsName As String, ByVal goldenSheet As String)
Dim ws As Worksheet: Set ws = Worksheets(wsName)
Dim g As Worksheet: Set g = Worksheets(goldenSheet)
Dim a As Variant: a = ws.Range("A1").CurrentRegion.Value
Dim b As Variant: b = g.Range("A1").CurrentRegion.Value
StartCase "GoldenCompare " & wsName
AssertArray2DEquals b, a, "現在出力はGoldenと一致するべき"
EndCase
End Sub
Private Function PrepareOut(ByVal name As String) As Worksheet
Dim s As Worksheet
On Error Resume Next: Set s = Worksheets(name): On Error GoTo 0
If s Is Nothing Then Set s = Worksheets.Add: s.Name = name
s.Cells.Clear
Set PrepareOut = s
End Function
VB重要ポイントの深掘り
ゴールデンは「仕様合意済みの期待出力」です。仕様変更の際はゴールデンを更新し、その変更理由を残します。行ごとの厳密一致で比較し、違いが出たらテストで赤く表示するなど、発見しやすくします。
依存の切り離しとモック(差し替え)
外部依存を関数経由にしておくと、テスト時は「モック(偽物)」に差し替え、安定した検証ができます。以下は時計とファイル読みを差し替える例です。
差し替え可能な依存とモック
' ModDeps.bas
Option Explicit
Public Function ClockNow() As Date
ClockNow = Now
End Function
Public Function ReadCsv(ByVal path As String) As Variant
' 実運用のCSV読込(省略)
End Function
' テスト用モック
Public Function ClockNow_Mock() As Date
ClockNow_Mock = #1/1/2025#
End Function
Public Function ReadCsv_Mock(ByVal path As String) As Variant
ReadCsv_Mock = SmallTable()
End Function
VB' Test_Deps.bas
Option Explicit
Public Sub Test_UsesMocks()
Dim t As Date: t = ClockNow_Mock()
StartCase "Clock Mock"
AssertEquals CDate("2025-01-01"), t, "固定時刻が返るべき"
EndCase
Dim a As Variant: a = ReadCsv_Mock("dummy")
StartCase "CSV Mock"
AssertEquals "Key", a(1, 1), "ヘッダがKeyであるべき"
EndCase
End Sub
VB重要ポイントの深掘り
依存を「関数名で差し替えるだけ」にすると、VBAでも十分モックが扱えます。実稼働コードは本依存、テスト中はモックに切り替えることで、ネットワークやファイルの遅延・不安定に影響されず、確実な検証が可能になります。
自動実行と合格基準の運用
ブックを開いたらテストを走らせ、合格しなければ分かる仕組みにします。毎回の結果はシートとメッセージで可視化し、失敗件数が残っている状態で配布しないルールにします。
自動起動のフック
' ThisWorkbook
Option Explicit
Private Sub Workbook_Open()
Register "Test_Validate_RequiredAndType"
Register "Test_GroupSum_ByKey"
Register "Test_SortStable_ByName"
Register "Test_UsesMocks"
RunAllTests
End Sub
VB重要ポイントの深掘り
毎回テストが走るだけで「壊れたまま出荷」が減ります。失敗を見逃さないために、「Failed>0なら警告表示」など、簡単でも良い合格基準を入れておきます。定時自動テストはWindowsタスクスケジューラでブックを開くだけでも実現できます。
まとめ:テストの“型”を先に入れて、配列中心で速く回す
アサーションとランナー、フィクスチャ、依存差し替え、ゴールデン比較を用意し、検証・集計・ソートのユニットテストから始めると、Excelでも品質保証は十分回ります。揺らぎを抑え、配列I/Oで高速化し、毎回自動で走る仕組みを整える。これが「拡張しても壊れない」ための最短ルートです。
