ささいなことですが。

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

Friendly GUI操作用上位ライブラリの必要性と思想

これは「Friendly Advent Calendar 2014 - Qiita」の記事です。

昨日は、GUI操作は生でやるとひどい目に遭うってとこまでやりました。

理由は

  • インテリセンスが効かない
  • そもそも、操作が複雑なものもある
  • 特にネイティブは・・・

通常時にプログラムで使っているコントロールとは言え、普段やらない操作も多いんですね。例えばTextBoxのText変更はみんなやるでしょうけど、DataGridViewの編集操作は知らない人もいるでしょう。知ってても複数行書く必要があるのは面倒ですよね。

そこで上位ライブラリですよ

つまり、昨日書いたようなコードは、結局みんな使うし、誰かが一度書いてラップしておけば良いのですよね。ということで、Friendlyコミュニティーで、ラップライブラリ(Friendlyを使う上位ライブラリ)を作成しておきました。
f:id:ishikawa-tatsuya:20141217221041p:plain

GUI操作用上位ライブラリの設計思想

GUI操作というコンテキストに特化

Friendlyの基本機能が対象プロセスの内部APIを何でも使えるという広すぎる機能を提供しているのに対して、こちらはGUIそれもWin32、WinForms、WPFと分けてかなりコンテキストを限定したものにしています。

必要最小限のインターフェイス

インターフェイスは可能な限り少なくしました。使うときの選択肢は可能な限り減らしたいのです。そのため、よく使うであろう操作のみの定義しています。「よく使う機能って何よ?」はい。すみません、これは私たちが勝手に決めましたw。でも大丈夫です。足りない機能は簡単に拡張できるのです。

高い拡張性

ラッパークラスは、対象プロセスのコントロールの参照を保有して(NativeStandardControlsはWindowハンドルのみ)、それをいつでも外部から参照できるようになっています。つまり、それを使えば何でもできるのですね。なので追加機能は、継承するなり、拡張機能にするなりで、付け足していただくことができます。それも自分達で一回ラップすればいいだけですよね。例えばWinFormsのボタンのラッパークラスは背景色を取得するメソッドを持っていません。もし背景色を取得するメソッドを追加したいと思ったら、これでできます。

public static class FormsButtonExtensions
{
    public static Color GetBackColor(this FormsButton button)
    {
        return (Color)button.Dynamic().BackColor;
    }
}

それから提供していないコントロールに関しても、同様にラッパークラスを作ることができます。例えば、標準では提供していませんが、グレープシティー様やインフラジスティックス様のコンポーネントですね。インフラジティクス様のグリッドに関してはブログで取り上げていただいております。
Daizen Ikehara : [WPF] テスト自動化 Friendly [Win]

EmulateXXX

ユーザー操作を模するメソッドはEmulateXXXという命名規則にしています。そして、これには同期と非同期の2パターン用意しています。と言うのは処理を実行したタイミングでモーダルダイアログが出る可能性があるからです。

現在提供している上位ライブラリ

詳細な紹介はまた、別のハンズオンでやります。

Friendly.Windows.Grasp

Codeer.Friendly.Windows.Grasp.dll - 株式会社Codeer (コーディア)
Windowやコントロールの取得、モーダルダイアログの取得処理が実装されています。

Friendly.Windows.NativeStandardControls

Codeer.Friendly.Windows.NativeStandardControls.dll - 株式会社Codeer (コーディア)
ネイティブの操作は、DLL公開関数のみなので正直、結構難しいです。でも一般的なものはラップしておきました。これで足りないときは自分でDLL公開関数を定義してGUIを飛ばして処理を実行する方がいいかな・・・。もちろん対象のコントロールの動きを把握していればそれを操作してもらってもOKです。

Friendly.FormsStandardControls

Ong.Friendly.FormsStandardControls.dll - 株式会社Codeer (コーディア)
WinForms用です。

Friendly.WPFStandardControls

Roommetro/Friendly.WPFStandardControls · GitHub
WPF用です。

Friendly.PinInterface

vshtc/Friendly.PinInterface · GitHub

これはGUIのラッパーライブラリではないのです。少し特殊です。型安全にFriendlyを使うための一つの解です。説明は長くなりますので別の回でやります。

ラッパークラスへの要望は受け付けます

これらのラッパーライブラリに関しては要望があれば、連絡ください。こちらで対応してライブラリを更新することもあります。

昨日のコードの書き直し

では昨日のコードを書き直してみます。
まずは参照の付けたし。
Friendly.Windows.Grasp、Friendly.FormsStandardControls、Friendly.Windows.NativeStandardControlsをNugetから取得します。
(Friendly.Windows.GraspはNativeStandardControlsにくっついてくるので以下の二個取得してください)
f:id:ishikawa-tatsuya:20141217220139p:plain

usingの付けたし。

using Ong.Friendly.FormsStandardControls;
using Codeer.Friendly.Windows.Grasp;
using Codeer.Friendly.Windows.NativeStandardControls;

で、コードはこんな感じ。

[TestMethod]
public void UpperLibraryTest()
{
    //各コントロールを操作しやすいようにラップ
    var form = new WindowControl(_form);
    var textBox = new FormsTextBox(_form._textBox);
    var dateTimePicker = new FormsDateTimePicker(_form._dateTimePicker);
    var dataGridView = new FormsDataGridView(_form._dataGridView);
    var button = new FormsButton(_form._button);

    //操作 インテリセンスが効く!
    textBox.EmulateChangeText("Codeer");
    dateTimePicker.EmulateSelectDay(new DateTime(2011, 3, 14));
    dataGridView.EmulateChangeCellText(0, 0, "ishikawa");
    var async = new Async();
    button.EmulateClick(async);

    //メッセージボックスを取得
    var msg = new NativeMessageBox(form.WaitForNextModal());

    //メッセージボックスのテキストをチェック
    Assert.AreEqual("Codeer\r\n2011/03/14\r\nishikawa", msg.Message);

    //OK
    msg.EmulateButtonClick("OK");

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

簡単になったでしょ?
詳細はまたハンズオンで説明します。
本来は、アプリケーションドライバという設計パターンを使ってさらにシンプルなシナリオにします。その辺もいずれハンズオンで紹介します。
でも、重要なのは、基本部分だけでも書けて、でも毎回書くのは面倒なんで、上位ライブラリを使おう。で、上位ライブラリって、その処理をラップしただけってことです。

明日へ続く・・・