C#ATIA

↑タイトル詐欺 主にFusion360API 偶にCATIA V5 VBA(絶賛ネタ切れ中)

配列のDebug.Assert

CATIA V5と言うより、VBAなお話です。

ちょっと行き詰まるところと申しますか、限界を感じたのでクラスを
作って対応しようと思っているところなのですが、作る以上単体テスト
っぽい事を行い、安全性を確認・確保しようと思っています。

ところが、これ自体で行き詰まるとは・・・。

問題は、数値のみの配列を返すメソッドが有り、想定外の戻り値の
場合はDebug.Assertでひっかけたいんです。

簡単な例で書くと、これです。

'vba
Option Explicit

Sub unit_test()
    Dim ary1 As Variant
    ary1 = Array(1, 2, 3)

    Debug.Assert ary1 = Array(1, 2, 3)

    MsgBox "Done"
End Sub

これで出来ると思ったのですが、駄目なんです・・・。
配列の比較は "=" じゃダメなんですねVBAは。

やったことが無かったのですが、pythonだと出来るようです。
Python で配列の一致比較 (list, ndarray) - Qiita


それじゃ、配列の中身が一致するまでを関数で行えば良いな
と思い、こんな感じにしてみました。

'vba
Option Explicit

Sub unit_test()

    Dim ary1 As Variant
    ary1 = Array(1, 2, 3)

    'Debug.Assert ary1 = Array(1, 2, 3)

    Call assert_with_array(ary1, Array(1, 2, 3))

    Call assert_with_array(ary1, Array(1, 2, 4))

    MsgBox "Done"

End Sub


Private Sub assert_with_array( _
    ByVal ary1 As Variant, _
    ByVal ary2 As Variant)

    Debug.Assert IsArray(ary1)

    Debug.Assert IsArray(ary2)

    Debug.Assert UBound(ary1) = UBound(ary2)

    Dim i As Long
    For i = 0 To UBound(ary1)
        Debug.Assert ary1(i) = ary2(i)
    Next

End Sub

一回目はクリアし、二度目は引っかかります。
”おぉOKじゃん” と思ったのですが、よく考えたら何処で
assert_with_array関数を呼び出して引っかけたのかが分かりません。

調べたところ、関数の呼び出し元を知る方法は、VBAには
無さそうです。
Redirecting
世間は厳しい・・・。

pythonであれば、tracebackモジュールで呼び出し元を得られます。
C#も何か有ったのハズですが、忘れました・・・。

そこで思い付いたのが、joinで文字列にして比較すれば良いのでは?

'vba
Option Explicit

Sub unit_test()

    Dim ary1 As Variant
    ary1 = Array(1, 2, 3)

    'Debug.Assert ary1 = Array(1, 2, 3)
    
'    Call assert_with_array(ary1, Array(1, 2, 3))

'    Call assert_with_array(ary1, Array(1, 2, 4))

    Debug.Assert Join(ary1, "") = Join(Array(1, 2, 3), "")
    
    Debug.Assert Join(ary1, "") = Join(Array(1, 2, 4), "")

    MsgBox "Done"

End Sub

お見事! 勝手に型変換を逆手に利用した方法です。
(この方法は、検索しても見つけられませんでした)
文字列や数値等限定ですが、まぁ何も無いより良いと思ってます。

世間の皆様はどうやっているのかな?(やっていないのだろうな・・・)

追記です!

良く考えたら、文字列比較はあれじゃNGなパターンがありました。
これです。

'vba
Option Explicit

Sub unit_test()

    Dim ary1 As Variant
    ary1 = Array(1, 2, 3)

    'Debug.Assert ary1 = Array(1, 2, 3)
    
'    Call assert_with_array(ary1, Array(1, 2, 3))

'    Call assert_with_array(ary1, Array(1, 2, 4))

'    Debug.Assert Join(ary1, "") = Join(Array(1, 2, 3), "")

'    Debug.Assert Join(ary1, "") = Join(Array(1, 2, 4), "")

    Dim ary2 As Variant
    ary2 = Array(1, 11, 111)

    Debug.Assert Join(ary2, "") = Join(Array(11, 1, 111), "")

    MsgBox "Done"

End Sub

せめてセパレータを入れる必要がありますね。

    Debug.Assert Join(ary2, "@") = Join(Array(11, 1, 111), "@")

さらに良く考えたら、チェックさせる関数内でDebug.Assertする
のではなく、判断した内容をBooleanで返せば良いだけの事でした。

'vba
Option Explicit

Sub unit_test()

    Dim ary1 As Variant
    ary1 = Array(1, 2, 3)

    'Debug.Assert ary1 = Array(1, 2, 3)
    
'    Call assert_with_array(ary1, Array(1, 2, 3))

'    Call assert_with_array(ary1, Array(1, 2, 4))

'    Debug.Assert Join(ary1, "") = Join(Array(1, 2, 3), "")

'    Debug.Assert Join(ary1, "") = Join(Array(1, 2, 4), "")

'    Dim ary2 As Variant
'    ary2 = Array(1, 11, 111)

'    Debug.Assert Join(ary2, "") = Join(Array(11, 1, 111), "")

'    Debug.Assert Join(ary2, "@") = Join(Array(11, 1, 111), "@")

    Debug.Assert assert_array(ary1, Array(1, 2, 3))

    Debug.Assert assert_array(ary1, Array(1, 2, 4))

    MsgBox "Done"

End Sub


Private Function assert_array( _
    ByVal ary1 As Variant, _
    ByVal ary2 As Variant) _
    As Boolean

    assert_array = True
    
    Select Case False
        Case IsArray(ary1)
            assert_array = False
            Exit Function
        Case IsArray(ary2)
            assert_array = False
            Exit Function
        Case UBound(ary1) = UBound(ary2)
            assert_array = False
            Exit Function
    End Select

    Dim i As Long
    For i = 0 To UBound(ary1)
        If ary1(i) <> ary2(i) Then
            assert_array = False
            Exit Function
        End If
    Next

End Function

Debug.AssertはFalseで止まるって考えれば、これが正解な気がしてます。