IEで自動テストを動かすときにハマったあれこれ

この記事は、Selenium/Appium Advent Calendar 2014の6日目の記事です。
前日は、myhrさんの「[Chromeでフルサイズのスクリーンショットを撮るためのパッチ](http://qiita.com/myhr/items/dff7cee30182ab56f737)」でした。

今回は、作成した自動テストをJenkinsで実行し、なおかつブラウザはIEを使用するという局面で
これまでハマったポイントについて紹介します。

### クリックができない

「FirefoxやChromeでは順調に動くテストがなぜかIEでは失敗する」というのは
多くの人が経験されていることだと思いますが、
中でもわりとメジャーだと思われるトラブルがこれです。

ごく普通の```WebElement#click()```がうまく動作しない。
テスト実行中の画面を見ていると、WebDriverが対象のボタンなりリンクなりを
捉えているように見える（色が変わってたりします）ものの、その状態で**「ガタガタ震えている」**ような状態。
うまく表現できなくてすみません。

こういった場合は、

* ```click()```を```sendKeys(Keys.ENTER)```に変えてあげる
* ```click()```の前に```sendKeys(Keys.CONTROL)```を入れてフォーカスを確実に移してあげる

といった対処でうまくいくことがあります。
実際の画面操作と微妙に違うことをしているのは気になりますが、
テストが全く動かなくなってしまうよりはマシかと。

下記のようなユーティリティを作ってあげることが多いです。

```java
public void click(WebElement element) {
  if (driver instanceof InternetExplorerDriver) {
    element.sendKeys(Keys.CONTROL);
  }
  element.click();
}
```

参考：http://stackoverflow.com/questions/5574802/selenium-2-0b3-ie-webdriver-click-not-firing


### 画面遷移が終わらない

最近直面した問題がこちら。事象としてIEでだけ確認したというだけで、
別にIEでだけ起きるさしたる理由があるわけではないのですが…
```WebElement#click()```で画面遷移が起こるときは大抵次の画面のロードが終わるまで
待ってくれるものだと思っていたのですが、実は正確にはそうではないようです。

以下がJavaDocのclick()メソッドの説明。

> Click this element. If this causes a new page to load, this method will attempt to block until the page has loaded. At this point, you should discard all references to this element and any further operations performed on this element will throw a StaleElementReferenceException unless you know the element and the page will still be present. If click() causes a new page to be loaded via an event or is done by sending a native event then the method will *not* wait for it to be loaded and the caller should verify that a new page has been loaded.

もしクリックで新しいページがロードされる場合は、次のページのロードが終わるまで待ってくれる。
ただし、何らかのイベントか絡んで新しいページがロードされる場合にはロード完了を待たないので、
完了したことを改めて確認（次のページの要素が表示されてることの確認とか）しなければいけない。
ということのようです。

つまり、aタグのhref属性のURLで遷移する場合や通常のフォームのsubmit（←ちゃんと確認してません）
以外のときはちゃんと自分で確認を入れてね、ということですね。

私はつい最近まで「ページロード後にAJAXで追加されるような要素以外はclick()完了時点でロードされている」
という仕様だと思い込んでいたため、ちょっと苦労しました。
PageObjectパターンを利用しているのであれば、次のようにコンストラクタでwaitを入れると楽です。

```java
public class HogePage {
  private WebDriver driver;
  private WebDriverWait wait;

  public HogePage(WebDriver driver) {
    this.driver = driver;
    wait = new WebDriverWait(driver, 10); // 秒数は適当。

    wait.until(ExpectedConditions.titleIs("hoge"));
  }
}
```

### スクリーンショットが真っ黒になる

Jenkinsを運用している場合、ビルドやテストの実行はJenkins本体ではなくスレーブが行い
マスタ側ではジョブのコントロールだけしているという方は多いと思います。
加えて、JenkinsサーバがLinuxの場合はIEのテストをするには別でWindowsマシンが必要になります。

そこで、WindowsマシンをJenkinsのスレーブとして登録しておいてそこでSeleniumのテストを動かす…
ということになるわけですが、そのWindowsマシンにリモートデスクトップで接続して設定をしていたところ
残念なことになりました。

私はいつもテストが失敗したときの解析が楽になるように失敗時の画面キャプチャを保存しているのですが、
テスト実行時にリモートデスクトップの接続を切っているとせっかくのキャプチャが真っ黒な画像になってしまいます。
これは接続を切ったときにWindows側で「画面をロックした」という扱いになり、
IEがヘッドレスモード（？）で動いてしまうためのようです。

参考：https://code.google.com/p/selenium/issues/detail?id=3536

参考リンクに色々と回避策が書いてあるのですが、ほとんどが「IEだとNG」になってしまったため
結局リモートデスクトップをやめてVNCを使うことに落ち着きました。
VNC（私はTightVNCというツールを使っています）でアクセスすると、
クライアント側からの接続を切っても問題なくキャプチャを取得することができました。

もちろん、こういった瑣末な問題にとらわれないために[SauceLabs](https://saucelabs.com/)や[BrowserStack](http://www.browserstack.com/)
といったサービスを利用するのも有効ですね。



***

すみません、何だかまとまりのない記事になってしまいました…
明日は、kuronekomichaelさんです。よろしくお願いします！