Help us understand the problem. What is going on with this article?

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

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

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

手法と技術的背景

ユニットテスト

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

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

Implements ステートメントと疑似的な継承

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

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

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

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

テストコード

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

依存コンポーネント (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つのメソッドにまとめて一括実行しましょう。

以上です。


  1. VB6 では自動テストが一般化しておらず、「単体テスト」というと「結合テスト」との対比で機能/コンポーネント単位の手動テストを指すことがあるため、ここではあえて日本の VB6 界であまり使われない「ユニットテスト」という表現を用います。 

CodeOne
【品質と生産性にこだわるシステム開発】 .NET(C#/VB.NET)専門・リモート開発歴10年。即日・1時間から頼める常駐しないエンジニア。確かな技術で開発チームを手堅くサポートいたします。
https://codeone.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした