RC から WebDriver への移行
Selenium WebDriver への移行方法
Selenium 2 を採用する際のよくある質問は、既存のテストセットに新しいテストを追加する場合に何をすべきかということです。フレームワークを初めて使用するユーザーは、新しい WebDriver API を使用してテストを書き始めることができます。しかし、すでに既存のテストスイートを持っているユーザーはどうでしょうか?このガイドは、既存のテストを新しい API に移行する方法を説明するように設計されており、すべての新しいテストを WebDriver が提供する新機能を使用して記述できるようにします。
ここで紹介する方法は、すべてを一度に大規模にプッシュする必要なく、WebDriver API への段階的な移行について説明しています。これは、既存のテストの移行により多くの時間をかけることができることを意味し、労力を費やす場所を決定しやすくする可能性があります。
このガイドは Java を使用して書かれています。これは、移行を容易にするための最良のサポートがあるためです。他の言語向けに優れたツールを提供する場合、このガイドはこれらの言語を含めるように拡張されます。
WebDriver に移行する理由
ある API から別の API へとテストスイートを移行するには、膨大な労力が必要です。あなたとあなたのチームがこの移行を検討するのはなぜでしょうか?WebDriver を使用するために Selenium テストを移行することを検討すべき理由をいくつかご紹介します。
- より小さく、コンパクトな API。WebDriver の API は、元の Selenium RC API よりもオブジェクト指向です。これにより、作業が容易になります。
- ユーザーインタラクションのエミュレーションの向上。可能な場合、WebDriver はネイティブイベントを利用して Web ページと対話します。これは、ユーザーがサイトやアプリを操作する方法をより忠実に模倣しています。さらに、WebDriver は高度なユーザーインタラクション API を提供しており、サイトとの複雑なインタラクションをモデル化できます。
- ブラウザベンダーによるサポート。Opera、Mozilla、Google はすべて WebDriver の開発に積極的に参加しており、それぞれがフレームワークの改善に取り組むエンジニアを擁しています。多くの場合、これは WebDriver のサポートがブラウザ自体に組み込まれていることを意味します。テストはできるだけ高速かつ安定して実行されます。
開始する前に
移行プロセスをできるだけ痛みのないものにするために、すべてのテストが最新の Selenium リリースで適切に実行されることを確認してください。これは当たり前のことのように聞こえるかもしれませんが、言っておくに越したことはありません!
はじめに
移行を開始する際の最初のステップは、Selenium のインスタンスを取得する方法を変更することです。Selenium RC を使用する場合、これはこのように行われます
Selenium selenium = new DefaultSelenium("localhost", 4444, "*firefox", "http://www.yoursite.com");
selenium.start();
これはこのように置き換える必要があります
WebDriver driver = new FirefoxDriver();
Selenium selenium = new WebDriverBackedSelenium(driver, "http://www.yoursite.com");
次のステップ
テストがエラーなしで実行されるようになったら、次の段階は、実際のテストコードを WebDriver API を使用するように移行することです。コードがどの程度抽象化されているかによって、これは短いプロセスまたは長いプロセスになる可能性があります。いずれにせよ、アプローチは同じであり、簡単に要約できます。編集するときに新しい API を使用するようにコードを変更します。
Selenium インスタンスから基盤となる WebDriver 実装を抽出する必要がある場合は、WrapsDriver にキャストするだけです
WebDriver driver = ((WrapsDriver) selenium).getWrappedDriver();
これにより、Selenium インスタンスを通常どおりに渡すことができますが、必要に応じて WebDriver インスタンスをアンラップできます。
ある時点で、コードベースは主に新しい API を使用するようになります。この時点で、関係を反転させ、WebDriver を全体で使用し、オンデマンドで Selenium インスタンスをインスタンス化します
Selenium selenium = new WebDriverBackedSelenium(driver, baseUrl);
よくある問題
幸いなことに、この移行を経験するのはあなたが初めてではありません。そのため、他の人が見てきた一般的な問題と、それらを解決する方法を紹介します。
クリックとタイピングはより完全
Selenium RC テストでよく見られるパターンは、次のようなものです
selenium.type("name", "exciting tex");
selenium.keyDown("name", "t");
selenium.keyPress("name", "t");
selenium.keyUp("name", "t");
これは、「type」が単に識別された要素の内容を置き換えるという事実に依存しています。ユーザーがページを操作した場合に通常発生するすべてのイベントも発生させることなく。最後の「key*」の直接呼び出しにより、JS ハンドラーが期待どおりに起動します。
WebDriverBackedSelenium を使用すると、フォームフィールドに入力した結果は「exciting texttt」になります。期待どおりではありません!その理由は、WebDriver がユーザーの動作をより正確にエミュレートするため、イベントをずっと発生させていることになります。
この同じ事実により、ページロードが Selenium 1 テストよりも早く発生する場合があります。WebDriver によって「StaleElementException」がスローされた場合に発生したことがわかります。
WaitForPageToLoad がすぐに戻りすぎる
ページロードが完了したタイミングを特定するのはトリッキーな作業です。ここで言うページロード完了とは、「load イベントが発生したとき」、「すべての AJAX リクエストが完了したとき」、「ネットワークトラフィックがないとき」、「document.readyState が変更されたとき」、または完全に別の何かを意味するのでしょうか?
WebDriver は、元の Selenium の動作をシミュレートしようとしますが、さまざまな理由から常に完全に機能するとは限りません。最も一般的な理由は、ページロードがまだ開始されていない場合と、メソッド呼び出し間でページロードが完了した場合の違いを区別するのが難しいことです。これにより、読み込みが完了した(または開始さえしていない!)後に制御がテストに返されることがあります。
これに対する解決策は、何か特定のものを待つことです。一般的に、これは次に操作する要素、または特定の値を設定する JavaScript 変数である可能性があります。たとえば、次のようになります
Wait<WebDriver> wait = new WebDriverWait(driver, Duration.ofSeconds(30));
WebElement element= wait.until(visibilityOfElementLocated(By.id("some_id")));
「visibilityOfElementLocated」が次のように実装されている場合
public ExpectedCondition<WebElement> visibilityOfElementLocated(final By locator) {
return new ExpectedCondition<WebElement>() {
public WebElement apply(WebDriver driver) {
WebElement toReturn = driver.findElement(locator);
if (toReturn.isDisplayed()) {
return toReturn;
}
return null;
}
};
}
これは複雑に見えるかもしれませんが、ほとんどすべてがボイラープレートコードです。唯一興味深い点は、「ExpectedCondition」が「apply」メソッドが「null」でも Boolean.FALSE のいずれでもないものを返すまで繰り返し評価されることです。
もちろん、これらの「wait」呼び出しをすべて追加すると、コードが煩雑になる可能性があります。そのような場合で、ニーズが単純な場合は、暗黙的な待機を使用することを検討してください
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
これを行うことで、要素が配置されるたびに、要素が存在しない場合、要素が存在するか、30 秒が経過するまで再試行されます。
XPath または CSS セレクターによる検索が常に機能するとは限らないが、Selenium 1 では機能する
Selenium 1 では、xpath がバンドルされたライブラリを使用するのが一般的でした。WebDriver は、代替手段がない限り、常にネイティブブラウザメソッドを使用します。つまり、一部のブラウザでは複雑な xpath 式が壊れる可能性があります。
Selenium 1 の CSS セレクターは、Sizzle ライブラリを使用して実装されました。これは CSS セレクター仕様のスーパーセットを実装しており、どこで一線を越えたのかが必ずしも明確ではありません。WebDriverBackedSelenium を使用していて、要素を見つけるために CSS セレクターの代わりに Sizzle ロケーターを使用している場合、警告がコンソールに記録されます。要素を見つけられないためにテストが失敗する場合は、特にこれらを探す時間を取る価値があります。
Browserbot はない
Selenium RC は Selenium Core に基づいており、そのため Javascript を実行すると、Selenium Core のビットにアクセスして物事を容易にすることができました。WebDriver は Selenium Core に基づいていないため、これはもはや不可能です。Selenium Core を使用しているかどうかをどのように判断できますか?簡単です!評価された Javascript で「selenium」または「browserbot」を使用しているかどうかを確認するだけです。
ブラウザボットを使用して、現在のウィンドウまたはテストのドキュメントへのハンドルを取得している場合があります。幸いなことに、WebDriver は常に現在のウィンドウのコンテキストで JS を評価するため、「window」または「document」を直接使用できます。
または、ブラウザボットを使用して要素を特定している場合があります。WebDriver では、これを行うためのイディオムは、最初に要素を特定し、それを JavaScript への引数として渡すことです。したがって
String name = selenium.getEval(
"selenium.browserbot.findElement('id=foo', browserbot.getCurrentWindow()).tagName");
このようになります
WebElement element = driver.findElement(By.id("foo"));
String name = (String) ((JavascriptExecutor) driver).executeScript(
"return arguments[0].tagName", element);
渡された「element」変数が、JS 標準の「arguments」配列の最初の項目として表示される方法に注目してください。
JavaScript の実行が何も返さない
WebDriver の JavascriptExecutor はすべての JS をラップして匿名式として評価します。つまり、「return」キーワードを使用する必要があります
String title = selenium.getEval("browserbot.getCurrentWindow().document.title");
このようになります
((JavascriptExecutor) driver).executeScript("return document.title;");