2012年12月22日土曜日

Selenium@Androidの罠

この記事では、私が遭遇したSeleniumの問題およびその解決策を共有することを目的としています。
Android向けにSeleniumを利用している方の参考になれれば幸です。


Selenium WebDriverはWebアプリケーションの自動テストツールとして有名です。
SeleniumとWebDriverは元々別ツールだったのですが、1つのツールとして統合され、どちらの機能も利用できるようになり便利になりました。

統合前のSeleniumはテストドライバのコアがHTML/javascriptで実装されていた。
テスト対象アプリと同じブラウザ上で実行させ、テスト対象アプリをjavascriptにより操作、情報取得を行っていた。
ドライバはHTML/jsベースなため、様々なブラウザで汎用的に利用できるとされている。





一方、WebDriverはドライバがブラウザのプラグイン等を利用して実装されている。
ブラウザのプラグイン仕様はブラウザの種類毎に異なるため、
ドライバをブラウザ毎に開発する必要があるが、
HTML/jsベースのSelenium1よりも豊富かつ高性能なコマンドを利用することができ、
テスト性に優れているとされる。

ドライバはInternetExploreDriver、FirefoxDriver、ChromeDriverはもちろんのこと、
AndroidDriverやIPhoneDriverまで用意されている。




















Selenium1とWebDriverは統合され、
Selenium WebDriver(Selenium2)となった。
統合と言っても、製品を単に抱き合わせただけではない。
WebDriverでSelenium1のエミュレーションが利用できるようになり、
WebDriverを利用しながらSelenium1の機能を利用することができるのだ。
この場合SeleniumRCの起動は不要となる。

Selenium2では
WebDriverBackedSelenium

という名のエミュレート化されたSeleniumインスタンスが利用できる。

ここで取得したSeleniumインスタンスを用いて、従来のSelenium1同等の機能が利用できる。
しくみは至って単純である。
エミュレートに対応しているドライバは、「任意のjavascriptをブラウザ上で実行する」という指示をブラウザ上のドライバ(Plugin)に行うことができる。
Selenium1時代にドライバとして利用されていたjavascriptをPluginに実行指示することで、エミュレーションを実現している。























以上が私が付け焼き刃で覚えたSelenium2の知識です。(間違いが含まれているかも・・・)
さて、前置きがだいぶ長くなりましたが、ここからが本題です。
仕事でAndroid、iPhone向けにSelenium2を利用して自動テストを実施することになったのですが、
Androidのテスト実行時に問題が発生し、数日間頭を悩ませられました。
結果的には解決したのですが、情報が少なく解決に苦労した。
同じ事象に悩んでいるかた向けに、その時のノウハウを共有したいと思います。
(iPhoneはまだ未実施なので対象外)
対象バージョンは
Selenium2.25
AndroidDriver2.21
です。

Selenium2ではAndroidDriverというドライバが用意されており、Android標準ブラウザ上で自動テストを走行させることができます。

(余談)
ちなみにこのAndroidDriver、Androidアプリ(apk)として用意されています。アプリ内のWebViewにテスト対象アプリの画面を表示するようになっています。
標準ブラウザにプラグインを組込む術が無かったのでこのような実装になっていると思われますが、
WebViewと標準ブラウザは動きが若干異なるので、テストとして完全ではないと思います。。。


さて問題となっている罠です。
まず試しに、WebDriver流の書き方のテストスクリプトで走行させてみると、正常に動きました。


正常に走行する例)

それを見て安心し、Selenium1時代のSelenium流の書き方のテストスクリプトを再利用し、
先ほど紹介したSeleniumエミュレーションで走行させたところ、assertionエラーやnullpoが発生してしまいました。


エラーが発生する例)


Selenium流の書き方なんて必要ないし、たいした問題じゃないでしょ、という意見もあるかもですが、
回帰テストとして古いテストスクリプトを流用する必要がある場合に、大変な問題になります。

エラーの原因は当初謎でしたが、同じスクリプトをドライバをFirefoxDriverやChromeDriverに変えて走行させると正しく動作するので、Android由来の問題なのは確実だと思いました。
ソースの静的解析、ステップ実行、Android側のログ監視を続けた結果、ドライバ@Android側でjavascriptの文法エラーが発生していることを確認ました。
(私はAndroidエミュレータ4.0でのみ確認しています。)

Unexpected token ILLEGAL ...

タイミングからして、ドライバ@サーバ側から送られたSelenium1のjavascriptをWebView上で実行しようとしたタイミングで発生しているようです。
他ブラウザでは正常に動作することから、そのjavascript自体に文法ミスがあるとは考え辛い。
疑うべきはWebViewのjavascript実行機能のバグ、もしくは制限。

Selenium1のjavascriptはHTML要素を検索したり、ボタン押下を指示したりするような処理が書かれたコードであるが、比較的ボリュームがある(30kb前後)。
ちなみにSelenium2のjarの以下に当jsファイルが格納されている。
org/openqa/selenium/internal/seleniumemulation/

で、原因の仮説を二つ立てました。
(1) WebVewで実行できるjavascriptのサイズ上限がある。
(2) WebVewで実行できるjavascriptの行数上限がある。

(2)はAndroidで出力されていた「Unexpected token ILLEGAL ...」のエラー箇所がいつも13行目を示していたため、
行数が関係しているのでは、という思いつきから立てました。

(1)は以下の情報より正しくないことが分かったので、あとは(2)を確かめるのみ。
http://stackoverflow.com/questions/5051670/is-there-any-limitation-on-urls-length-in-androids-webview-loadurl-method

そこで
org/openqa/selenium/internal/seleniumemulation
配下の全jsの全改行文字を取っ払って、jarを作り直し、エラーとなっていたテストスクリプトを走行させたところ、
見事エラーが解消されました!
どうやら仮説(2)が正しかったようです。
Selenium2の問題というよりかはAndroid自体の問題のようですね。


最後に、当問題についてまとめます。

・Selenium2.25
・AndroidDriver2.21
で、WebDriverBackedSelenium(Selenium1のエミュレーション)を利用したテストスクリプトをAndroid上で走行させると、エラーが発生しテストが正しく行われない可能性がある。

原因は、Android側にエラーログ「Unexpected token ILLEGAL ...」が出力されていることから、
AndroidのWebViewが多数の改行を含むjavascriptを正常に実行できないことにあると推測された。

Selenium2のjar内にある、
org/openqa/selenium/internal/seleniumemulation/
配下のjsファイルの改行をすべて除去したjarで差し替えることにより、
当問題を回避することができた。

0 件のコメント:

コメントを投稿