Excel VBAを使った新しい関数のテスト結果確認の方法を現在の環境で使うなら「A1:A100が同じ文字列かを見るのではなく、test case tableからinputとexpectedを読み、functionを呼び、actual・error・pass/failを記録します。blank、boundary、invalid、errorを含め、全case数とfailed数で判定します。」が基本方針です。対象はproduction workbookとは別copyで、input、expected、tolerance、actual、pass/failを一行一caseにするケースです。最初の確認を読み取りだけに限定し、変更後に何を照合し、どの証跡から戻すかまで明記します。 確認ポイント:各caseには期待値、許容差、期待errorを持たせ、変更したApplication状態は終了経路にかかわらず復元します。
test case表と対象関数の境界を固定する
VBA関数テストは、入力値、期待値、実値、エラー番号、合否を一ケース一行で残します。元ブックを複製し、外部ファイルやシートを変更しないテスト用データで正常系・境界値・エラー系を実行します。一件で例外が起きても次へ進め、最終的に総数が成功数と失敗数の合計に一致することを確認します。
- functionの仕様、input型、return型、error契約を確認する
- test workbookとproduction dataを分離する
- expected値と数値toleranceをreviewする
- Application stateとexternal dependencyを固定する
変更前の関数版と期待値を保存する
testはproduction workbookやDBを変更しないcopyで行います。functionがmail、file、DB update等のside effectを持つ場合はdependencyをstub化し、本番endpointへ接続しません。実行前backupを取り、Application stateを必ず復元します。失敗時はtest outputだけを破棄し、production dataへ修正を自動反映しません。
失敗case後も次のcaseを実行できるか確認する
- 全cellをExpectedValue一つと比較する
- 一件失敗で残りcaseを実行しない
- errorをResume Nextで隠す
- floating pointを完全一致する
- production dataでtestする
入力・実行・記録を一caseずつ分離する
CaseIdとExpectedをtableで定義する
' tblTests columns: CaseId, Input1, Input2, Expected, Tolerance, Actual, ErrorNumber, Passed
' case IDはuniqueにする
expectedをcodeへ一つだけ直書きしません。
数値と文字列で比較規則を切り替える
actual = FunctionUnderTest(input1, input2)
If IsNumeric(actual) And IsNumeric(expected) Then
passed = Abs(CDbl(actual)-CDbl(expected)) <= tolerance
Else
passed = (CStr(actual)=CStr(expected))
End If
型とtoleranceを仕様に合わせます。
Err.Numberをcase結果へ格納する
On Error GoTo CaseFail
' call function and set actual/passed
WriteResult:
' write Actual, ErrorNumber, Passed
GoTo NextCase
CaseFail:
errNo = Err.Number: errText = Err.Description
actual = "#ERROR: " & errText: passed = False
Resume WriteResult
NextCase:
' continue with the next ListRow
On Error Resume Nextで全errorを隠しません。
pass・fail件数を合計する
passedCount=WorksheetFunction.CountIf(testTable.ListColumns("Passed").DataBodyRange,True)
failedCount=testTable.ListRows.Count-passedCount
Debug.Print testTable.ListRows.Count,passedCount,failedCount
一件不一致で残りtestを止めず全caseを記録します。
Application.Calculationを必ず復元する
oldCalc=Application.Calculation
On Error GoTo CleanFail
Application.Calculation=xlCalculationManual
' tests
CleanExit:
Application.Calculation=oldCalc
Exit Sub
CleanFail:
Resume CleanExit
ScreenUpdatingやEnableEventsも変更した場合は元へ戻します。
totalとpass+failの一致を機械判定する
文字列、date、floating point、Excel error、Empty、Nullは比較方法が違います。数値の完全一致を要求すると丸め差を誤判定し、toleranceを広くしすぎると不具合を見逃します。testがpassしても未定義caseやexternal data変化は残ります。functionのside effectがないかも確認します。
一時状態を戻してから結果sheetを保存する
normal、boundary、blank、invalid、error、locale/dateのcaseを用意し、故意に一件expectedを変えてharnessがfailを検知することも確認します。case合計=pass+fail、unique ID、actual列、error列を照合し、再実行で同じ結果になることを確認します。
関数test完了の合格基準
tblTestsの各rowを読み、FunctionUnderTestのactual、Err.Number、Passedを同じrowへ書きます。case error時はResume WriteResultでhandlerを抜けた後、GoTo NextCaseで次rowへ進み、active errorなしのResumeや未定義labelを使いません。
case単位で継続するtest runner
Option Explicit
' 動作確認用。実案件ではこの関数だけを対象関数呼出しへ置き換える。
Private Function FunctionUnderTest(ByVal input1 As Variant, ByVal input2 As Variant) As Variant
If CStr(input1) = "RAISE" Then Err.Raise vbObjectError + 2201, , "sample error"
FunctionUnderTest = CDbl(input1) + CDbl(input2)
End Function
Public Sub RunFunctionTests()
Dim testTable As ListObject, lr As ListRow, ids As Object
Dim input1 As Variant, input2 As Variant, expected As Variant, actual As Variant
Dim tolerance As Double, passed As Boolean, errNo As Long, errText As String
Dim caseId As String, passedCount As Long, failedCount As Long
Dim oldCalc As XlCalculation, stateCaptured As Boolean
On Error GoTo FatalFail
oldCalc = Application.Calculation
stateCaptured = True
Application.Calculation = xlCalculationManual
Set testTable = ThisWorkbook.Worksheets("Tests").ListObjects("tblTests")
Set ids = CreateObject("Scripting.Dictionary")
For Each lr In testTable.ListRows
caseId = Trim$(CStr(lr.Range.Cells(1, testTable.ListColumns("CaseId").Index).Value2))
If Len(caseId) = 0 Or ids.Exists(caseId) Then Err.Raise vbObjectError + 2202, , "Blank or duplicate CaseId: " & caseId
ids.Add caseId, True
input1 = lr.Range.Cells(1, testTable.ListColumns("Input1").Index).Value
input2 = lr.Range.Cells(1, testTable.ListColumns("Input2").Index).Value
expected = lr.Range.Cells(1, testTable.ListColumns("Expected").Index).Value
tolerance = CDbl(lr.Range.Cells(1, testTable.ListColumns("Tolerance").Index).Value)
actual = Empty: errNo = 0: errText = "": passed = False
On Error GoTo CaseFail
actual = FunctionUnderTest(input1, input2)
If IsNumeric(actual) And IsNumeric(expected) Then
passed = Abs(CDbl(actual) - CDbl(expected)) <= tolerance
Else
passed = (CStr(actual) = CStr(expected))
End If
WriteResult:
On Error GoTo FatalFail
lr.Range.Cells(1, testTable.ListColumns("Actual").Index).Value = actual
lr.Range.Cells(1, testTable.ListColumns("ErrorNumber").Index).Value2 = errNo
lr.Range.Cells(1, testTable.ListColumns("Passed").Index).Value = passed
If passed Then passedCount = passedCount + 1 Else failedCount = failedCount + 1
GoTo NextCase
CaseFail:
errNo = Err.Number
errText = Err.Description
actual = "#ERROR: " & errText
passed = False
Resume WriteResult
NextCase:
Next lr
Debug.Assert testTable.ListRows.Count = passedCount + failedCount
Debug.Print "total=" & testTable.ListRows.Count, "passed=" & passedCount, "failed=" & failedCount
CleanExit:
If stateCaptured Then Application.Calculation = oldCalc
Exit Sub
FatalFail:
MsgBox "RunFunctionTests failed: " & Err.Number & " " & Err.Description, vbExclamation
Resume CleanExit
End Sub
このcodeは画面表示だけを見るための例ではありません。通常系では「全caseにActual/ErrorNumber/Passedが入り、total=passed+failedになる」を確認し、0件と実行errorを別の結果として保存します。
成功・期待不一致・例外発生を分類する
- 期待どおり:全caseにActual/ErrorNumber/Passedが入り、total=passed+failedになる
- 0件・非適用:test table 0行はNoCasesとしてapplication stateを復元して終了する
- 実行error:個別function errorはそのcaseをFalseにして継続し、table/schema等のfatal errorは全体を停止する
Resume NextCaseはlabel不在ならcompile errorで、Resume CaseDone後に再びResumeするとerror 20になり得ます。期待値比較ではnumeric toleranceとstring exactを分け、Application.Calculation等を必ず元へ戻します。
tolerance境界・文字列・例外を試す
' 正常数値、tolerance境界、文字列、FunctionUnderTestがerrorを投げるcaseを用意し、
' error case後も次caseが実行されtotal=passed+failedになることを確認する。
0件、pass/fail、tolerance丁度、type違い、個別error、fatal schema errorをtestします。CaseId、expected/actual、error number、Passed、集計、復元後Calculationを保存します。
Excel VBAを使った新しい関数のテスト結果確認の方法の証跡には、実行対象と取得時刻に加え、通常・0件・errorのどれへ分類したかを残します。通常系は「全caseにActual/ErrorNumber/Passedが入り、total=passed+failedになる」、停止系は「個別function errorはそのcaseをFalseにして継続し、table/schema等のfatal errorは全体を停止する」を判断文としてそのまま作業票へ写し、担当者ごとの言い換えで意味が変わらないようにします。
VBA testを反復実行する場合は、CaseIdの重複をschema errorとして先に停止し、各caseのactual、型、許容差、Err.Numberを独立したrowへ出します。一つのcaseで起きたerrorを後続caseのexpectedへ流用せず、fatal errorと通常failを別集計にします。全case終了後にCalculationを復元できたことを確認し、未実行caseがあればpass率だけでreleaseを許可しません。

コメント