RubberDuckからのメッセージ「DefaultMemberRequired」は本質じゃないかも

# あらすじ

[RubberDuck](https://rubberduckvba.com/)を使うと、ExcelのVBAでテスト駆動開発ができます。
とても便利なので、今も幸せな気分で利用させていただいています。
これからも使い続ける予定です。

デバッグしていればよくあることですが、
素直にエラーメッセージの内容を追って失敗した経緯です。

あるとき、自作クラスのテスト中にRubberDuckさんから、
エラーメッセージを受け取りました。
このメッセージに紐づいたURLの先に、
「行ったっきり」になって時間を無駄にしました。

一部始終を書き残して、自分みたいな人が同じことをするのを未然に防ぎます。

# テスト対象のクラス

```vb:TextReader.cls
'@Folder("MainModule")

Option Explicit
Private SlicerStrings() As String
Private Const myTargetArray As String = "X,Y,Z,I,J,K"
Private Const PrimarySeparater As String = ","

Private Sub Class_Initialize()
    SlicerStrings = Split(myTargetArray, PrimarySeparater)
End Sub

Public Static Function GetStrings() As String()
    GetStrings = SlicerStrings
End Function
```

# テスト

上記のクラスをテストしようとしました。

## エラーになるテスト

このテストを実行すると、先ほどのエラーが出ます。

エラーメッセージが出るのは、
`Test_GetStrings()`の中の`Assert.AreEqual reader.GetStrings()(1), " 軸"`という部分です。

```vb:test.bas
Option Explicit
Option Private Module

'@TestModule
'@Folder("Tests")

Private Assert As New Rubberduck.AssertClass
'Private Fakes As New Rubberduck.FakesProvider
Private reader As New TextReader

'@ModuleInitialize
Private Sub ModuleInitialize()
    Set reader = New TextReader
    'this method runs once per module.
End Sub

'@ModuleCleanup
Private Sub ModuleCleanup()
    'this method runs once per module.
    Set Assert = Nothing
    Set reader = Nothing
End Sub

'@TestInitialize
Private Sub TestInitialize()
    'This method runs before every test in the module..
End Sub

'@TestCleanup
Private Sub TestCleanup()
    'this method runs after every test in the module.
End Sub

'@TestMethod("Small")
Private Sub Test_GetStrings()
    On Error GoTo TestFail
    'Arrange:
    'Act:
    'Assert:
    Assert.AreEqual reader.GetStrings()(1), "Y"
TestExit:
    Exit Sub
TestFail:
    Assert.fail "Test raised an error: #" & Err.Number & " - " & Err.Description

End Sub
```

# エラーメッセージを早とちり

## メッセージの内容

テストを行っているとRubberduckさんから[DefaultMemberRequired](https://rubberduckvba.com/inspections/details/DefaultMemberRequired)と言われました。
このメッセージには[サイト](https://rubberduckvba.com/inspections/details/DefaultMemberRequired)のリンクが付いています。

さっそくリンクをクリックして、その先を見てみると、以下のような説明がありました。

> Locates indexed default member calls for which the corresponding object does not have a suitable suitable default member.

Google翻訳すると「対応するオブジェクトに適切な適切なデフォルトメンバーがない、インデックス付きのデフォルトメンバー呼び出しを検索します。」でした。

## デフォルトメンバーとは？

`Worksheet.Item(1)`と書くところを、`Worksheet(1)`と書いても大丈夫ですよね。
このItemを省略できるのは、デフォルトメンバーとしてあらかじめ決められているからだそうです。

デフォルトメンバーという名称があるのは知りませんでした。
私もちょっと賢くなりました。

どうやら、クラス定義でデフォルトメンバーを指定してあげるとよいらしい。
そこでデフォルトメンバーに指定する方法を探し始めました。
しかし、それが誤りの始まりでした。

# デフォルトメンバーを定義してみよう

- [[VBA]クラスを定義する（ちょっと応用）](https://qiita.com/Kamo123/items/a39c8f3ffdc5e665de98)
- [[VBA]自作クラスに説明を追加する](https://qiita.com/nukie_53/items/9998ba12bd3c80780fb4)

世の中にはすごいテクニックをつかう人たちがいます。
クラス定義に`Atrribute`を設定すると上手くいくみたいです。

しかし、私はサボリーマンです。
この方法は手順が回りくどく、やりたくないです。
そこで、別の方法を探すことにしました。
（難しくて理解できなかっただけ）

# なぜエラーになったのか

上記テストコードで`reader.GetStrings()(1)`という部分に注目してください。

PythonやC++などでは、`reader.GetStrings()[1]`と書いたり`reader.GetStrings[1]`と書いたりできますよね。
それと同じだと思って`reader.GetStrings(1)`としてみます。
するとVBAの文法では、配列ではなく関数に`1`を渡すことになるので、エラーになります。

VBAのfunctionでは引数の定義が無い場合は呼び出すときに括弧を省略できます。
また、関数の引数を指定する括弧と、配列の添え字（インデックス）を指定する括弧が同一です。
この２つ仕様を同時に当てはめなければならないとき、混乱が生じやすいです。

RubberDuckでは、この混乱を避けるため、関数の引数が空の時には必ずエラーとして処理するようになっています。

# 修正後のクラス

問題のテスト`Test_GetStrings()`を修正して、エラーが出なくなったのがこちらです。

クッションとなる変数を作成し、段階を追って処理することで、
文法の衝突を避けています。

```vb:
'@TestMethod("Small")
Private Sub Test_GetStrings()
    On Error GoTo TestFail

    'Arrange:
    Dim myarray() As String
    'Act:
    myarray = reader.GetStrings
    'Assert:
    Assert.AreEqual myarray(1), "Y"

TestExit:
    Exit Sub
TestFail:
    Assert.fail "Test raised an error: #" & Err.Number & " - " & Err.Description

End Sub
```

# RubberDuckの先輩

RubberDuckの先輩たちによるRubberDuckの紹介です。

- [VBAにはユニットテストやリファクタリング機能がない・・・そんなふうに考えていた時期が俺にもありました](https://qiita.com/mima_ita/items/3629006163d0a7fa6e55)
- [VBA開発環境をモダンにする - RubberduckとVBEThemeColorEditorの紹介](https://qiita.com/mochimo/items/e9be36619a76e15bc898)

大変お世話になりました。

# まとめ

エラーメッセージ通りに解釈して突き進むと、
`GetStrings()`自体を修正する道が待っています。
その道は、深く険しい道のりでした。
しかし、今回は割と簡単な解決策がありました。
まっすぐだけでは時間がもったいないこともあるようです。

RubberDuckについて誤解が無いように、最後に宣伝をします。
[RubberDuck](https://rubberduckvba.com/)を使ってストレスフリーな開発環境を手に入れましょう！

**Excelsior!**