Friendlyで始めるwindowsアプリシステムテスト自動化+内部使用技術解説

9,792 views
1,958 views

Published on

2014/08/23 わんくま横浜勉強会の資料

Published in: Software
0 Comments
5 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
9,792
On SlideShare
0
From Embeds
0
Number of Embeds
507
Actions
Shares
0
Downloads
9
Comments
0
Likes
5
Embeds 0
No embeds

No notes for slide
  • PrivateでもOK
  • Friendlyで始めるwindowsアプリシステムテスト自動化+内部使用技術解説

    1. 1. Friendlyで始める Windowsアプリ システムテスト自動化 + 内部使用技術解説
    2. 2. 石川達也 株式会社Codeer代表取締役 Microsoft MVP for C# Windowsアプリテスト自動化歴9年 自己紹介
    3. 3. システムテスト自動化って? 文字通り人間の代わりに アプリケーションをプログラムが動かして 成否判定をすることです。
    4. 4. お得? コストを抑えたらね (・∀・) エラー! 成功 開発期間中、 実行し続ける。 デグレを早期に検出。 テストの作りが悪くて不安定 仕様変更等でメンテ 作成 指定のケースではデグレがなかった という情報を取得できた! BugFix 実行
    5. 5. OS層 Winコア(User32,Kernel32とか) MS,他ベンダGUI ユーザ実装 ←SendInput 不安定、遅い ←Win32Api ←UIAutomation 難しい 操作できないのもある GUIアプリをプログラムからどうやって操作するの?
    6. 6. ところで、同一プロセスからだったら操作できるでしょ? //当たり前 void Operation() { _comboBox.SelectedIndex = 1; }
    7. 7. そこで、Friendlyですよ!
    8. 8. Friendly 他のとは根本的に違います。 対象プロセスと、友達になって、 まるで、同一プロセスでプログラムするように 我が物顔で操作できるのです! 最強
    9. 9. ①君のものは僕のもの public partial class MainForm : Form { ComboBox _comboBox; string MyFunc(int value) { return value.ToString(); } } public void YourThingIsMine() { var process= Process.GetProcessesByName("Target")[0]; //友達になると・・・ var app = new WindowsAppFriend(process); //別プロセスのオブジェクトを //自分のプロセスのもののように操作できる。 dynamic form = app.Type<Application>().OpenForms[0]; form._comboBox.SelectedIndex = 1; string ret = form.MyFunc(3); } そ、そんな・・・ .Netはもちろん NativeのDLL公開関数もOK!
    10. 10. ②僕のものは君のもの void MyThingIsYours() { var process= Process.GetProcessesByName("Target")[0]; var app = new WindowsAppFriend(process); //自分のコードを動的にインジェクション! WindowsAppExpander.LoadAssembly(app, GetType().Assembly); //挿入したコードを相手プロセスで実行 app.Type(GetType()). ForTest(); } static void ForTest() { /*テスト用*/ } え!? 勝手に?
    11. 11. 上位ライブラリ紹介 基本 内部メソッド操作、DLLインジェクション Win32 WinForms WPF (めとべや) GUI操作ライブラリ PinInterface (VSHTC) 記述性UP 拡張も自由自在! Friendlyの上に構築されているから安定感抜群! //各コントロールをラップする //シンプルで直感的なインターフェイスのみ定義されている var _comboBox = new FormsComboBox(form._comboBox);
    12. 12. デモ
    13. 13. Codeer で検索 eが一個多い これらはNugetで無料で入手できます!
    14. 14. 自動化環境 + + VSもTest作成だけならExpressでOK + Friendly Friendly.Windows Friendly.Windows.NativeStandardControls Friendly.FormsStandardControls Friendly.WPFStandardControls Friendly.PinInterface VSTest
    15. 15. ということは? ・ツール購入コスト無料 ・簡単なインターフェイス、既知のインターフェイス →作成コスト減 ・安定感抜群 →運用コスト減 ・テスタビリティーを容易に操作 →作成、メンテコスト減 ・高速な動作 →実行時間減
    16. 16. 備考) テスタビリティー操作(自動化と相性悪いコード) //ここの結合は不安が少ない void Event(object sender, EventArgs e) { EventCore(PointToClient(Control. MousePosition)); } //これをFriendlyで呼び出す void EventCore(Point mousePosClient) { //ここから先のロジックをテストしたい。 } プロダクトを変更。 難易度高くて効果の低い 部分は自動化しない。 効果の高い部分のみ 呼び出せるようにする。 ・キー、マウス直接参照 ・D&D ・OS提供のGUI etc… ・タイマ ・非同期 ・ペイントイベントを利用したトリッキーコード etc…
    17. 17. コスパに優れた自動テスト構築が可能! 明日からでも自動化しよう!
    18. 18. ・・・ 知ってますよ。 わんくま横浜は、 こんなことじゃ満足しないんでしょ?
    19. 19. 本邦初公開! 友達の作り方
    20. 20. 最終的にはリフレクションを使っています。 でも、普通は自プロセスにしか使えないですよね? //いくら探してもみつからない foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { var type = assembly.GetType(“Target.ClassA”); } //当たり前だけど、自分のプロセスのFormしか見つからない var forms =(FormCollection)typeof(Application).GetProperty("OpenForms"). GetGetMethod().Invoke(null, new object[0]);
    21. 21. ということは、こうですよ。 リフレクション実行! Dll Injection! これに話しかける。 関数名、引数を渡す。 あれ? テストプロセスと友達だった気がする…
    22. 22. Win32APIを使ってDLLインジェクション //インジェクションできるのはネイティブDLLのみ //対象プロセスにメモリ作成 IntPtr path = VirtualAllocEx(...); //ロードさせたいパスを書き込み WriteProcessMemory(path, ...); //LoadLibraryのアドレスを取得 //★Kernel32は常に同じアドレスにロードされる! IntPtr pFunc = GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryW"); //LoadLibraryを対象プロセスで実行! CreateRemoteThread(..., pFunc, path, ...); 入りました!
    23. 23. ところで、 CreateRemoteThreadに渡す関数ポインタの型 CreateRemoteThread(..., pFunc, ...); //pFuncの型はコレに合致するもの typedef DWORD (__stdcall *LPTHREAD_START_ROUTINE) ( [in] LPVOID lpThreadParameter ); //あれ?戻り値・・・ HMODULE LoadLibraryW( LPCWSTR lpFileName ); 64bitのとき、合ってないやん! ∑( ゚Д゚ノ)ノ
    24. 24. HMODULE Func(LPCWSTR) { HMODULE m = nullptr; return m; mov rax,qword ptr [rsp] } int _tmain(int argc, _TCHAR* argv[]) { LPTHREAD_START_ROUTINE f = (decltype(f))Func; auto ret = f(nullptr); call qword ptr [f] mov dword ptr [ret],eax return 0; } 実験してみました! 戻り値使わなかったら 全く問題なし (゚∀゚;) HMODULEを戻り値に使っても レジスタしかつかわない 情報が落ちただけ
    25. 25. 挿入したDLLのAPIを呼び出し //自分のプロセスで関数ポインタを取得 IntPtr mod = LoadLibrary(dllPath); IntPtr proc = GetProcAddress(mod, procName); //差分を計算 var distance = …;//proc - mod; x64とx86で型が違う //相手プロセスの中でのDLLのアドレスを取得 IntPtr targetDllAddress; EnumProcessModules(...) ... //差分を足したら、対象プロセス内での関数ポインタになる IntPtr pFunc = …;//targetDllAddress + distance; //指定の関数を対象プロセスで実行! CreateRemoteThread(..., pFunc, path, ...); Init() 初期化開始! 通信サーバー 立ち上げるよー
    26. 26. 呼び出された関数内で.Netのアセンブリをロード //ホストAPIを使って.Netの機能呼び出し ICLRMetaHost *pMetaHost; ICLRRuntimeInfo *pRuntimeInfo; ICLRRuntimeHost *pClrRuntimeHost; CLRCreateInstance(... , IID_PPV_ARGS(&pMetaHost)); //pRuntimeInfoの検索 //ルールはWindowsAppFriendのコンストラクタ参照 ... //CLR開始 pRuntimeInfo->IsLoadable(...); pRuntimeInfo->GetInterface(... , IID_PPV_ARGS(&pClrRuntimeHost)); pClrRuntimeHost->Start(); //.Netのアセンブリのロードと目的のメソッド呼び出し pClrRuntimeHost->ExecuteInDefaultAppDomain (asm, type, method, args, &ret); もちろん.Netの機能が いるよねー。
    27. 27. .Netのアセンブリインジェクションで気を付けること //アセンブリの解決 AppDomain.CurrentDomain.AssemblyResolve += ...; アセンブリの解決! GAC、プロービングパス以外のDLLは、 中途半端にしか使えない。 AssemblyResolveで解決 ハマりポイント!
    28. 28. 後は、通信サーバーを立ち上げる 僕たち、 友達になりました! サーバーと言っても単なるWindow WM_COPYDATAを使うと リッチなデータが送受信できる! SendMessageでデータもらってます。 でも、COM対策はしてるので例の ルールは気にしなくてOK!
    29. 29. 求む! Friendlyエバンジェリスト 今日実演したデモができるセットをご用意いたしております。 Friendlyのデモは手品のようなので、 LTや宴会でやったら、うけること間違いなし! また、会社に導入したいとき、上司に見せると効果アリ!? こちらからダウンロードできます。 http://www.codeer.co.jp/download
    30. 30. お知らせ 登壇予定 9/20 Boost.勉強会 #16 大阪 http://osakaboostjp.doorkeeper. jp/events/14150 9/11 SQIPシンポジウム http://www.juse.jp/sqip/symp osium/timetable/day1/
    31. 31. ご清聴ありがとうございました。 明日からでもFriendlyで自動化を始めましょう! http://www.codeer.co.jp/AutoTest picture Dawn Huczek

    ×