Nunit で ActiveXコンポーネントのメソッドをテストする

プロジェクトで使ってる地図用のActiveXコンポーネント (VB6用!) が、ここ数日おかしな値を返すため、検証用に単体テストを作ることにしました。その備忘録です。
もともと VB6 用のコンポーネント .NET で使うのはどうよ?という話もあるのですが、代替品が見つからないし、ベンダーさんが .NET 用ライブラリを作るの待ってられないので、苦肉の策で使用してます。
それでもここ数年は問題なかったのが、先日からおかしな値を返すようになったので、面倒だけどちと小細工してテスト作ることになった訳です。でもどうやって ActiveXインスタンスを表示せずに生成するかが肝ですね。肝は以下の三つ・・・

  • OcxState プロパティに設定するリソースを用意する
  • CreateControl メソッド実行してから他のプロパティ設定
  • STAThread 属性は必須

以下、手順です。


まず NUnit を入手してインストールします。現時点の最新バージョンは 2.6.2 です。NUnit-2.6.2.msi をダウンロードしインストールしてください。
NUnit Downloads


クラスライブラリプロジェクトを新規作成。参照設定に NUnitFramework を追加します。
次にプロジェクトに Form を追加。ActiveX のリソースを取るためなので Form をテストで使うことはありません。Form じゃなく UserControl でもいいと思います。
Form を追加したら、ツールボックスから ActiveX を貼り付けます。保存して Form のデザイナを閉じましょう。


ActiveXインスタンスを返却するメソッドを用意します。ここで注意すべきは、VB6 用に開発された ActiveX はシングルスレッドでしか動作しないため、ファクトリメソッドもテストメソッドも STAThread 属性が必須です。また ActiveX のプロパティは OcxState を設定し CreateControl で実行しない限り設定できません。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using AxHogeLib;
using NUnit.Framework;

[TestFixture]
class AxHogeOCXTest {
    [STAThread]
    private AxHogeOCX CreateHogeOCX() {
        AxHogeOCX hoge;
        try {
            // Form1 のリソースを生成
            var resources = new ComponentResourceManager(typeof(Form1));

            hoge = new AxHogeOCX();
            // 以下の二行が肝です。
            hoge.OcxState = (AxHost.State)resources.GetObject("AxHogeOCX.OcxState");
            hoge.CreateControl();
            // 以降、プロパティを設定する
        }
        catch (Exception) {
            throw;
        }
        return hoge;
    }
}


後はテストメソッドでインスタンスを返して、テストするだけです。STAThread を忘れずに。

[STAThread]
[TestCase("東京都千代田区", "千代田", TestName = "東京都千代田区 - 千代田")]
[TestCase("東京都中央区", "日本橋", TestName = "東京都中央区 - 日本橋")]
[TestCase("東京都港区", "赤坂", TestName = "東京都港区 - 赤坂")]
[TestCase("東京都新宿区", "新宿", TestName = "東京都新宿区 - 新宿")]
[TestCase("東京都文京区", "本駒込", TestName = "東京都文京区 - 本駒込")]
・・・・
public void SearchLowerPlaceName_下位地名リスト取得の検証_都内23区(string placeName, string town) {
    using (var hoge = this.CreateHogeOCX()) {
        var result = hoge.SearchLowerPlaceName(placeName);
        if (!result) {
            throw new ArgumentException("下位地名リストの取得に失敗しました!");
        }

        var placeList = new List<string>();
        for (int i = 0; i < hoge .PlaceNameListCount; i++) {
            placeList.Add(hoge .get_PlaceNameList(i));
        }

        // 住所の存在確認
        Assert.That(placeList.Contains(placeName + town),
            "失敗した住所リストの先頭:" + hoge.get_PlaceNameList(0));
    }
}

まだまだ ActiveX の呪縛から逃れられそうもないですぅ(--