Selenium + COM でIE対応
IEのテスト自動化をしていて、IEDriverServerが落ちる現象が多発していました。
こんなのですね。
ポストバックが発生してその後重たいJavaScriptが動くような処理のトリガをSeleniumで実行したとき、もしくはその直後の操作で発生していました。とは言え、この情報はググってもあんまり見かけなかったし、たまたま今回のテスト対象が相性悪かっただけなのかな・・・。
で、どうにも解決しなかったので、その処理だけCOMでやることにしました。
IEはアウトプロセスCOMなので、Friendly使わなくとも別プロセスから操作できますね。(残念w
このサイトがよくまとまってて参考になりました。ありがとうございました。
IE - ClockAhead 開発Blog
参照させてもらいつつ、差分で書いていきます。
Seleniumで起動したIEを見つける
基本はSeleniumで操作するので、もちろん起動はSeleniumで行います。
なので、起動したIEを見つけてくる必要があります。
ShellWindowsというクラスを使うとIEを列挙することができます。
で、その中からURLを使って検索する。
こんな関数を作っておくと便利ですね。
static InternetExplorer FindIE(string url) { var windows = new ShellWindows(); InternetExplorer ie = null; for (int i = 0; i < windows.Count; i++) { ie = windows.Item(i) as InternetExplorer; if (ie != null && ie.LocationURL == url) { return ie; } } throw new NotFoundException(); }
これでSeleniumと相互運用できます。
//IWebDriver driver;
var ie = FindIE(driver.Url)
でもこれでは、同一のURLを持ったIEが複数あると破綻しますね。
なので、起動する前にIEを全部終了させます。
ついでにIEDriverServerのゾンビがいたらそれも消します。
Process.GetProcessesByName("IEDriverServer").ToList().ForEach(e => e.Kill()); Process.GetProcessesByName("iexplore").ToList().ForEach(e => e.Kill());
んー、他に特定するいい方法ないかな?知ってる人いたら教えて下さい。
待ち合わせ
C#でIEを自動制御しよう (6) ページの読み込み完了まで待機する - ClockAhead 開発Blog
これは非常に助かりました。InternetExplorerDriverだけ、ページの読み込みを待ってくれないことがあったのですよね。そんな時はこれを併用することで上手くまつことができました。
Script実行
こんな感じでJavaScriptを実行できます。
static void ExecuteScript(IWebDriver driver, string script) { var ie = FindIE(driver.Url); var doc = ie.Document as IHTMLDocument2; doc.parentWindow.execScript(script); }
でも、このJavaScript実行はSeleniumと比べて不便ですね。
Seleniumのいいところは、IWebElementをJavaScript内でも使えるところです。
//IWebDriver driver; //IWebElement element; driver.ExecuteScript("arguments[0].click()", elemnt);
これができるようにしたいですね。
document.allとsourceIndexを使えば近いことができそうです。
まずはSeleniumを使って、document.all内のインデックスを見つけてきます。
static long FindIndexInAlls(IWebDriver driver, IWebElement element) { return (long)driver.ExecuteScript( "var index = arguments[0].sourceIndex;" + "for (var i = 0; i < document.all.length; i++) {" + "if (document.all[i].sourceIndex == index) {" + "return i;" + "}" + "}" + "return -1;" , element); }
そのインデックスを使えば、COMのJavaScript実行からもそのelementにアクセスすることができます。
//IWebDriver driver; //IWebElement element; var index = FindIndexInAlls(driver, element); ExecuteScript(driver, "document.all[" + index + "].click()");
それからSystem.Windows.Forms.SendKeysも使いました。
COM経由の操作ではSendKeysがないのですよね。
SendKeysは理論的には100%ではないので最終手段なのですが、Selenium経由でやってればIEはアクティブになってるし、その最中にPC触らなければ、今のところ確実に成功しています。
element.Click();
System.Windows.Forms.SendKeys.SendWait("{ENTER}");
もちろんラップしてつかってくださいね。
テストシナリオの中にこんな複雑な構文入れるのはNGです。それに「IEだったら」とかいう分岐もなしにしたいですね。なのでドライバレイヤに隠ぺいすることがおすすめです。