VB6 でも(簡易的・擬似的ではありますが)ユニットテスト1を実装することができます。
モックオブジェクトを利用することも可能です。
依存コンポーネント (Depend-On Component)として、Part 1 にコードを掲載した VB6 版 StringBuilder
クラスを使用しますので、初めにそちらをご覧ください。
ほかに特別なサードパーティコンポーネントは使いません。
手法と技術的背景
ユニットテスト
以下の手順でユニットテストを実装・実行できます。
- テスト用の標準モジュールを作成します。
- テスト用の
Public
メソッドを定義します。 - テストメソッド内に対象となる処理を記述します。
-
Debug.Assert
で処理結果を検証します。 - イミディエイトウィンドウからテストメソッドを実行します。テストメソッド呼び出しを集めたメソッドを作成しておくと一括実行できます。
Implements ステートメントと疑似的な継承
VB6 には Implements
という、インターフェイスの実装をするためのステートメントが用意されています。
.NET や Java の「インターフェイス」とは以下のような点で異なる、簡易的なゆるい仕組みです。
- インターフェイスとして指定されるのはただのクラスである。
- 実装メソッドは {InterfaceClass}_{Method} という、イベントハンドラのような形式の名称で定義する。
- 実装メソッドは既定では
Private
のため、実装クラス型の変数から実装メソッドに直接アクセスすることはできない(Public
に変えたり、Public
メソッドを通してアクセスすることはできる)。
この Implements
ステートメントのゆるい点をポジティブに活用すると、次のような方法で擬似的な継承を実現することができます。
- インターフェイスとして使用するクラスを基底クラスと位置づけ、共通ロジックを記述する。
- 実装クラスのメンバ変数(フィールド)で、インターフェイスとして使用するクラスのインスタンスを保持する。
- 実装メソッドでは、必要に応じて「2」のインスタンスから本来のメソッドを呼び出しつつ、処理を上書きする。
テストコード
以下は、この方法で作成したモッククラスを利用したユニットテストの例です。
Part 1 の StringBuilder
クラスを使用します。
依存コンポーネント (Depend-On Component)のテスト
まず StringBuilderTest
という標準モジュールを作成して、依存コンポーネント StringBuilder
のテストを記述します。
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
的に本来の処理を呼び出すことも可能です。
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
メソッドが定義されています。
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
オブジェクトがテキストを処理します。
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つのメソッドにまとめて一括実行しましょう。
以上です。
-
VB6 では自動テストが一般化しておらず、「単体テスト」というと「結合テスト」との対比で機能/コンポーネント単位の手動テストを指すことがあるため、ここではあえて日本の VB6 界であまり使われない「ユニットテスト」という表現を用います。 ↩