前回の続きです。
あと、ひと手間かけてやりましょう。それは対象プロセスのデバッグです。
デグレ検出→調査したい
自動検査でデグレを検出しました!作った甲斐がありましたね!
では調査しましょう。もちろん対象プロセスはデバッガから起動したもので調査したいですね。
あれ?EXE起動から始まってる・・・
あー、今の方式だと、EXEを起動させてテスト実行開始してますね。まあ、アタッチすればよいのですが面倒です。それに、ゆっくりデバッグしてたらタイムアップで終了させられてしまいますよねw。
こんな仕様でどうでしょうか?
調査中は勝手にプロセス終了されたくないですよね。情報が減るので。ではこれで実装してみましょう。AppDriverのプロセス管理の部分を通常用とデバッグ用に分けたらいいですね。こんな時はストラテジーパターン的なアプローチがいいですね。
通常用とデバッグ用で異なる部分を抽出し、インターフェイス化して切り分けるようにします。差分が出るのは、プロセスのライフサイクル管理の部分ですね。
using Codeer.Friendly; using Codeer.Friendly.Dynamic; using Codeer.Friendly.Windows; using Codeer.Friendly.Windows.Grasp; namespace EmployeeManagementDriver { publicclass AppDriver { IAppDriverCore _core; WindowsAppFriend _app; public MainFormDriver MainForm { get; private set; } publicbool IsDebug { get { return _core is AppDriverDebug; } } public AppDriver() { _core = AppDriverDebug.Exists ? (IAppDriverCore)new AppDriverDebug() : new AppDriverNormal(); } publicvoid Attach() { _core.Attach(); _app = new WindowsAppFriend(_core.Process); MainForm = new MainFormDriver(new WindowControl(_app, _core.Process.MainWindowHandle)); InitApp(); } publicvoid SetTimeup(int time) { _core.SetTimeup(time); } publicvoid Release(bool isContinue) { _app.Dispose(); _core.Release(isContinue); } privatevoid InitApp() { MainForm.ListBoxEmployee.Dynamic().Items.Clear(); } publicvoid EndProcess() { _core.EndProcess(); } } }
差分はこんな感じでインターフェイス化されます。
using System.Diagnostics; namespace EmployeeManagementDriver { interface IAppDriverCore { Process Process { get; } void SetTimeup(int time); void Attach(); void Release(bool isSuccess); void EndProcess(); } }
通常時用ですね。
前回のコードから抽出しただけです。
using System.Diagnostics; namespace EmployeeManagementDriver { class AppDriverNormal : IAppDriverCore { public Process Process { get; private set; } Killer _killer; publicvoid Attach() { if (Process == null) { Process = Process.Start("EmployeeManagement.exe"); } _killer = new Killer(1000 * 60 * 5, Process.Id); } publicvoid Release(bool isContinue) { if (isContinue) { _killer.Finish(); } else { EndProcess(); } } publicvoid SetTimeup(int time) { _killer.Timeup = time; } publicvoid EndProcess() { try { _killer.Finish(); } catch { } try { Process.Kill(); } catch { } Process = null; } } }
デバッグ用です。.Netアプリの場合は[vshost]ってのが名前につくのでそれで区別しましょう。ネイティブアプリの場合はまた別の方法でやってみてください。まあこれはプロジェクトに応じた方法で。
これは終了処理系は何にもやらないですね。
デバッガで起動しているものを勝手に終了させる必要はないのです。
using System; using System.Diagnostics; using System.Linq; namespace EmployeeManagementDriver { class AppDriverDebug : IAppDriverCore { public Process Process { get; private set; } publicstaticbool Exists { get { return GetDebugProcess() != null; } } publicvoid Attach() { Process = GetDebugProcess(); } publicvoid Release(bool isContinue) { } publicvoid SetTimeup(int time) { } publicvoid EndProcess() { } static Process GetDebugProcess() { return Process.GetProcessesByName("EmployeeManagement.vshost").Where(e => e.MainWindowHandle != IntPtr.Zero).FirstOrDefault(); } } }
EmployeeManagementのslnファイルを作る
面倒だったんで一つのソリューションでやりましたが、デバッグを考えるとプロダクトだけのソリューションファイルがあった方がいいですね。ソリューションファイルを二つメンテするのはちょっと手間ですが、プロジェクトファイルを追加するときに両方に追加するだけなので、まあアリかなと。
嫌な場合は、テストコードと共有する型を含んだdllだけ参照するとかもアリです。実際そうしているお客さんもいます。
まあ、今回はslnファイルをもう一つ作りましょう。次の手順で作れます。
①一旦VisualStudioを閉じる
②HandsOn13.slnの拡張子を一回消す
③EmployeeManagement.csprojをダブルクリック
④全保存ボタンを押す→slnファイルを任意の名前で保存する
⑤HandsOn13の拡張子を戻す
②の手順がいるのは、このファイルあったらEmployeeManagement.csprojから起動しても、そのslnファイルを使っちゃうんですよねー。
では、EmployeeManagement.exeをデバッグ実行してテストを実行してみてください。
ちゃんとそのEXEにアッタッチされましたよね?
備考
それから、もう一つ、このサンプルでの手抜きを言っておくと、プロセス起動でEXEを直接参照しているので、同一フォルダにEXEがあって、それを起動していますが、実際はこんなことしないですよ。どこか別のフォルダ置かれたファイルを起動させます。パスは相対パスにして、そのルールはプロジェクトごとに決めてください。
これで、一旦アプリケーションドライバの話は終わりです。
どうだったでしょうか?
気が付いたかもしれませんが、Friendly関係ない部分も多かったですね。
アプリケーションドライバ作成時はこの辺の.Netの知識も必要になってきます。
でも、それは開発者ならおそらくは知っているはずのことです。
(だって、対象アプリ作りましたよね?)
だから、その人たちに作ってもらいましょう。
長く説明しましたが、ボリューム自体はそんなに大きくないですね。
最終形はこちらからダウンロードできます。
次回からはテストシナリオを書いてみます。
はい。ここまでで作ったアプリケーションドライバを使ってテストシナリオを書いてみます。
アプリケーションドライバの実装を頑張ったので、そちらは簡単に作れますよー。