読者です 読者をやめる 読者になる 読者になる

ささいなことですが。

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

Friendly 操作対象プロセス内のインスタンス

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

今日は操作対象プロセス内のインスタンスについて考えます。
当たり前ですけど、普通はそんなものを直接操作するなんてできません。
しかし!
それができるのが魔法のライブラリFriendlyなのです!
まあ、昨日ネタはばらしましたがw

まずはこのコードを見てください。
これで、取ってきたFormのタイトルが変わります。

using(var app = new WindowsAppFriend(process)) 
{
    dynamic form = app.Type<Application>().OpenForms[0];
    form.Text = "friendly!";
}

ココで何が起こっているか詳細に見ていきます。

dynamic form = app.Type<Application>().OpenForms[0];

細分化します。

//①タイプに関する操作をするクラスの取得
dynamic dynamicType = app.Type<Application>();

//②相手プロセスでstaticなOpenFormsを実行して、それを取得
dynamic formCollection = dynamicType.OpenForms;

//③フォーム取得
dynamic form = formCollection[0];

DynamicAppType

まず①ですね。
これはタイプに関する操作を実行するDynamicAppTypeというクラスが取れます。
これを通して操作を実行すると、相手プロセスでそれが実行されるのです。
できることは、staticな操作とオブジェクトの生成です。
ちなみにこれはこんな感じで書くことが可能です。
どれでも取得できるものは同じです。

dynamic dynamicType = app.Type<Application>();
dynamic dynamicType = app.Type(typeof(Application));

//テストプロセスから相手プロセスのタイプを参照できない場合
dynamic dynamicType = app.Type().System.Windows.Forms.Application;
dynamic dynamicType = app.Type("System.Windows.Forms.Application");

で、オブジェクト生成です。

//相手プロセスの中にオブジェクトの生成もできるよ。
dynamic listBoxType = app.Type<ListBox>();
dynamic listBox = type();//コンストラクタ

次に②ですね。
相手プロセスでstaticメソッドのApplication.OpenFormsが実行されるのです。
で戻ってくるのは、相手プロセスのオブジェクトです。

DynamicAppVar

次の操作でformCollectionは何かというとDynamicAppVarというクラスです。

dynamic formCollection = dynamicType.OpenForms;

そして、これを使うと、もちろんそのインスタンスを操作できるのです。
③ですね。

//コレクションにアクセス
//相手プロセス内部で実行される
//また相手プロセスのインスタンスの参照を取得できる
dynamic form = formCollection[0];

参照の実体は取ってきた時点ではまだ相手プロセスにあります。
で、そのオブジェクトがシリアライズ可能な場合は、実際の型にキャスト、もしくは代入することによって、そのコピーを取得できます。シリアライズできないものは当然コピーに失敗します。

//実はこの時点では、stringの実体は相手プロセスにある。
dynamic text = form.Tex;
//ここで転送されてくる
string value = text;

DynamicAppVarに関しては、もう一つ重要な特徴があって、
このまま、関数の引数に渡せるのです。
そしたら、シリアライズでこっちにとって来たりせずに、
相手プロセスの中だけで操作できるのですね。
以下の例は、操作対象プロセスの中にListBoxを生成して、それをFormに追加するコードです。
ListBoxはもちろんシリアライズできないですよね。
仮にできても意味が変わってしまいます。

//相手プロセス内にリストボックス作成
dynamic listBox = app.Type<ListBox>()();
//フォームのコントロールに追加
//実体のコピーとか実施されずに
//相手プロセス内だけで処理完了
form.Controls.Add(listBox);

ちなみに引数にはシリアライズできるオブジェクトも渡せます。
その場合は、コピーされて相手プロセスに転送されます。
で、戻り値ですが、すべてDynamicAppVarで返ってきます。
プロパティーとかフィールドも同じですね。

テストで、その値を判定するとかの場合はキャストか代入してテストプロセスに転送してくる必要があります。
まあ、言葉にするとなんだか難しいですが、コードにすると普通のことです。

//これで値がコピーされてくる
string text = form.Tex;
Assert.AreEqual("friendly!", text);

この二つ理解したら、基本はOKっす。
シンプルでしょ?

図を付けておきまーす。

DynamicAppTypeとDynamicAppVarの処理の流れ

f:id:ishikawa-tatsuya:20141206133300p:plain

引数にDynamicAppVarを使う

f:id:ishikawa-tatsuya:20141206133259p:plain

ま、実際シンプルなものなんで、
少し手を動かすとすぐに慣れます。
慣れると、普段やってるプログラムの感覚で相手プロセスを操作できるのですね。
明日からはハンズオンにうつりまーす。