見出し画像

【今さら極める VB6 -Part 2】VB6 でもユニットテスト/モック利用

VB6 でも(簡易的・擬似的ではありますが)ユニットテストを実装することができます。
モックオブジェクトを利用することも可能です。

依存コンポーネント (Depend-On Component)として、Part 1 にコードを掲載した VB6 版 StringBuilder クラスを使用しますので、初めにそちらをご覧ください。
ほかに特別なサードパーティコンポーネントは使いません。

手法と技術的背景

ユニットテスト
以下の手順でユニットテストを実装・実行できます。

テスト用の標準モジュールを作成します。
テスト用の Public メソッドを定義します。
テストメソッド内に対象となる処理を記述します。
Debug.Assert で処理結果を検証します。
イミディエイトウィンドウからテストメソッドを実行します。テストメソッド呼び出しを集めたメソッドを作成しておくと一括実行できます。

Implements ステートメントと疑似的な継承
VB6 には Implements という、インターフェイスの実装をするためのステートメントが用意されています。
.NET や Java の「インターフェイス」とは以下のような点で異なる、簡易的なゆるい仕組みです。

・インターフェイスとして指定されるのはただのクラスである。
・実装メソッドは {InterfaceClass}_{Method} という、イベントハンドラのような形式の名称で定義する。
・実装メソッドは既定では Private のため、実装クラス型の変数から実装メソッドに直接アクセスすることはできない(Public に変えたり、Public メソッドを通してアクセスすることはできる)。

この Implements ステートメントのゆるい点をポジティブに活用すると、次のような方法で擬似的な継承を実現することができます。

1. インターフェイスとして使用するクラスを基底クラスと位置づけ、共通ロジックを記述する。
2. 実装クラスのメンバ変数(フィールド)で、インターフェイスとして使用するクラスのインスタンスを保持する。
3. 実装メソッドでは、必要に応じて「2」のインスタンスから本来のメソッドを呼び出しつつ、処理を上書きする。

テストコード

以下は、この方法で作成したモッククラスを利用したユニットテストの例です。
Part 1 の StringBuilder クラスを使用します。

依存コンポーネント (Depend-On Component)のテスト
まず StringBuilderTest という標準モジュールを作成して、依存コンポーネント StringBuilder のテストを記述します。

StringBuilderTest.bas

Option Explicit

'Length_SingleLine
Public Sub TestStringBuilder_Length_SingleLine()
   With New StringBuilder
       Debug.Assert .Length = 0
       Call .Append("")
       Debug.Assert .Length = 0
       Call .Append("あい")
       Debug.Assert .Length = 2
   End With
End Sub

'Length_MultiLine
Public Sub TestStringBuilder_Length_MultiLine()
   With New StringBuilder
       .LineSeparator = adLF
       Call .AppendLine("Line")
       Debug.Assert .Length = 5
   End With
End Sub

'ToString
Public Sub TestStringBuilder_ToString()
   With New StringBuilder
       .LineSeparator = adLF
       Call .Append("Code")
       Debug.Assert .ToString() = "Code"
       Call .AppendLine("One")
       Debug.Assert .ToString() = "CodeOne" & vbLf
   End With
End Sub

テスト実行
テストメソッドはイミディエイトウィンドウから実行します。
成功すればそのままカーソルが下の行に移動します。
検証に失敗すると、失敗した位置で実行が止まります。

依存コンポーネント (Depend-On Component)のモッククラス
次に StringBuilder のモッククラス MockStringBuilder を作成し、ユニットテスト向けに処理を上書きします。
ここでは Append, AppendLine に渡された引数値を保持し、ToString() では ToStringReturn で渡された文字列を返すようにします。
LineSeparator プロパティで実装しているように、base フィールドを使用して Moq の CallBase 的に本来の処理を呼び出すことも可能です。

MockStringBuilder.cls

Option Explicit

'インターフェイス
' StringBuilder 型で扱えるよう、同じインターフェイスを StringBuilder_{Method} 形式で実装する。
Implements StringBuilder

'委譲インスタンス
' 擬似的な継承を実現するため、インスタンスを保持する。
Private base As New StringBuilder

'モック用のフィールド
Private mAppendCalledArgs As New Collection
Private mAppendLineCalledArgs As New Collection
Private mToStringReturn As String

'Append に渡された引数配列のコレクション
Public Property Get AppendCalledArgs() As Collection
   Set AppendCalledArgs = mAppendCalledArgs
End Property

'AppendLine に渡された引数配列のコレクション
Public Property Get AppendLineCalledArgs() As Collection
   Set AppendLineCalledArgs = mAppendLineCalledArgs
End Property

'ToString で返す値
Public Property Get ToStringReturn() As String
   ToStringReturn = mToStringReturn
End Property
Public Property Let ToStringReturn(ByVal s As String)
   mToStringReturn = s
End Property

'改行文字
Private Property Let StringBuilder_LineSeparator(ByVal e As LineSeparatorsEnum)
   base.LineSeparator = e
End Property
Private Property Get StringBuilder_LineSeparator() As LineSeparatorsEnum
   StringBuilder_LineSeparator = base.LineSeparator
End Property

'文字列の長さ
Private Property Get StringBuilder_Length() As Long
   Debug.Assert False
End Property

'文字列を追加する。
Private Function StringBuilder_Append(ByVal s As String) As StringBuilder
   '渡された引数を保持する。
   Call mAppendCalledArgs.Add(Array(s))
End Function

'改行付で文字列を追加する。
Private Function StringBuilder_AppendLine(Optional ByVal s As String = "") As StringBuilder
   '渡された引数を保持する。
   Call mAppendLineCalledArgs.Add(Array(s))
End Function

'文字列化する。
Private Function StringBuilder_ToString() As String
   'モック用の値を返す。
   StringBuilder_ToString = mToStringReturn
End Function

テスト対象システム(System Under Test)
StringBuilder を使用するクラス Sample です。
テキストを返す GetText メソッドが定義されています。

Sample.cls

Option Explicit

Private mBuilder As StringBuilder

Public Property Get Builder() As StringBuilder
   Builder = mBuilder
End Property

Public Property Set Builder(ByVal o As StringBuilder)
   Set mBuilder = o
End Property

Public Sub Class_Initialize()
   Set mBuilder = New StringBuilder
End Sub

Public Function GetText() As String
   Call mBuilder.Append("Sample Calls StringBuilder")
   GetText = mBuilder.ToString()
End Function

テスト対象システム(System Under Test)のユニットテスト
Sample クラスの GetText メソッドをテストするのに MockStringBuilder を使用します。
GetText() を呼ぶと、本来の StringBuilder に変わって MockStringBuilder オブジェクトがテキストを処理します。

SampleTest.bas

Option Explicit

'GetText
Public Sub TestSample_GetText()
   
   'モックオブジェクトに戻り値を設定
   Dim oMockBuilder As New MockStringBuilder
   oMockBuilder.ToStringReturn = "MockStringBuilder Returns"
   
   With New Sample
       Set .Builder = oMockBuilder
       
       '対象メソッド実行
       Dim sActualText As String
       sActualText = .GetText()
       
       '1回目の Append 呼び出しの第1引数を検証
       Debug.Assert oMockBuilder.AppendCalledArgs(1)(0) = "Sample Calls StringBuilder"
       
       'AppendLine は呼び出されていないことを検証
       Debug.Assert oMockBuilder.AppendLineCalledArgs.Count = 0
       
       '戻り値を検証
       Debug.Assert sActualText = "MockStringBuilder Returns"
   End With

End Sub

テスト実行
イミディエイトウィンドウからテストコードを実行します。

?TestSample_GetText

通りましたね。
テストメソッドが増えたら、呼び出しを1つのメソッドにまとめて一括実行しましょう。

以上です。

この記事が気に入ったらサポートをしてみませんか?