ささいなことですが。

Windowsアプリテスト自動化ライブラリFriendly開発者の日記です。

Friendlyハンズオン9 Win32用上位ライブラリ

今回はWin32の話です。
プロダクトによっては、過去の資産を持っていて、一部ネイティブで作っているものもありますよね。
そうでなくて完全な.Netのアプリでも、実はネイティブの画面は出てくるのです。
と言うことで、ネイティブの画面も操作できるようにしておく必要がありますね。

FriendlyはネイティブのDLL公開関数も実行させることができます。
ネイティブDLL公開関数の呼び出し - 株式会社Codeer (コーディア)
ただ、.Netに比べてかなり面倒です。
なので、一般的なコントロール操作に関してはラップしたライブラリを用意しています。
Friendly.Windows.NativeStandardControlsです。これをご利用ください。
APIリファレンス
ソースコード

対応表

Windowクラス Friendly.Windows.NativeStandardControls
Button NativeButton
ComboBox、ComboBoxEx32 NativeComboBox
SysDateTimePick32 NativeDateTimePicker
Edit、RichEdit20A、RichEdit20W NativeEdit
IPAddress32 NativeIPAddress
ListBox NativeListBox
SysListView32 NativeListControl
SysMonthCal32 NativeMonthCalendar
msctls_progress32 NativeProgress
ScrollBar NativeScrollBar
msctls_trackbar32 NativeSlider
msctls_updown32 NativeSpinButton
SysTabControl32 NativeTab
SysTreeView32 NativeTree

特殊なものとして、メッセージボックスに対応したクラスもあります。

特殊 Friendly.Windows.NativeStandardControls
メッセージボックス NativeMessageBox

ハンズオンの練習用プロジェクト

こちらからダウンロードしてください。ブロック解除もお願いします。
https://github.com/Ishikawa-Tatsuya/HandsOn9
対象アプリはMFCで作っています。
ビルドできない人もいるかもしれないので、ソースコードも入れていますが、exeも作っています。

対象はMFCApplication.exeです。
ListBoxとTreeViewがあります。
f:id:ishikawa-tatsuya:20150107230118p:plain

OKボタンを押すと、メッセージボックス(モーダル)が表示されます。
f:id:ishikawa-tatsuya:20150107230430p:plain

Resource.hには次のように定義されています。
ListBoxとTreeViewにそれぞれ割り当たっているダイアログIDです。あ、OKボタンにはIDOKが割り当たっています。

#define IDC_LIST                        1000
#define IDC_TREE                        1001

テストプロジェクトはいつものように参照設定とusingまで終わっています。
自分のテストに組み込む場合はNugetからFriendly.Windows.NativeStandardControlsを取得してください。

ハンズオン開始

ListBox

まずはリストボックスです。
選択を変えてみましょう。

[TestMethod]
public void TestListBox() 
{
    var listBox = new NativeListBox(_dlg.IdentifyFromDialogId(IDC_LIST));
    listBox.EmulateChangeCurrentSelectedIndex(3);
    Assert.AreEqual(3, listBox.CurrentSelectedIndex);
}

.Netの場合はラッパークラスのコンストラクタの引数に対象アプリ内のコントロールのオブジェクトを渡していました。しかし、ネイティブの場合はそれがありません。渡すものは次のいずれかです。

  • Windowハンドル
  • WindowControl

今回の場合はWindowControlを渡しています。
それはIdentifyFromDialogIdを使って取得しています。ネイティブの場合はダイアログIDから取得するのが一番いいですね。これなら対象アプリで変えないようにコントロールしやすいです。
特定すると、後はWinFormsのものと同じような感覚で使えます。

TreeView

次はTreeViewです。
ノードの文字列を編集してみます。

[TestMethod]
public void TestTreeView()
{
    var treeView = new NativeTree(_dlg.IdentifyFromDialogId(IDC_TREE));
    var node = treeView.FindNode("0", "2");
    treeView.EmulateEdit(node, "x");
    Assert.AreEqual("x", treeView.GetItemText(node));
}

これも特定方法は同じですね。
NativeTreeのインターフェイスはネイティブのTreeViewのインターフェイスを少し意識した感じになっています。ノードはIntPtrで返ってきます。

メッセージボックス

少し特殊です。
コントロールではなくメッセージボックスをラップしたものです。

[TestMethod]
public void TestMessageBox()
{
    //OKボタンを非同期で押す
    var buttonOK = new NativeButton(_dlg.IdentifyFromDialogId(IDOK));
    var async = new Async();
    buttonOK.EmulateClick(async);

    //メッセージボックスを取得してラップ
    var msg = new NativeMessageBox(_dlg.WaitForNextModal());
    
    //メッセージを取得
    Assert.AreEqual("Msg", msg.Message);
    
    //テキストからボタンを検索して押す
    msg.EmulateButtonClick("OK");

    //非同期処理の完了待ち
    async.WaitForCompletion();
}

メッセージボックスはほとんどのアプリで使います。なので、このクラスは頻繁に使うと思います。

ラッパーを使う分にはネイティブでもさほど変わりませんね

特定方法は異なりますが、ラッパーを使うとネイティブのアプリでも割と簡単に操作できます。
でも、本気でWin32のアプリを自動化するには恐らくこれだけでは足りなくて、独自に定義したDLL公開関数を呼び出したりも必要になってくると思います。その辺もおいおいやっていきますが一旦.Netアプリ操作をメインで解説していきます。

次回は「.Netアプリで出てくるネイティブウィンドウ対応」のハンズオンです。