SeleniumとJenkinsでテスト自動化を作る中で色々詰まったことメモ。 あとAutoItとか。


Selenium Advanced Calendarを見てると「今はSeleniumもDockerとか色々使って楽に出来るようになった」って感じの文面を見かけて、自分が作った時はどうだったかな~ってことをふと思いだした。

........................

その時はSeleniumもJenkinsも使い始めだったこともあって、苦労の連続だった気がする...
ついでなので、備忘録兼ねて書くことにしました。

自動化環境とおおざっぱなテストの条件

Jenkins
Selenium + Java + Gradle

自動化したいこと

  • Seleniumでのテスト
  • WindowsNativeApplicationのテスト
    • WindowsNativeApplicationテストはSelenium側の成果物をテストのインプットにする必要があった

なので両方の連携が必須(なのでSelenium Gridが使えなかった...)
ブラウザは色々使ってテストしたい

楽にするためにやったこと

  • WindowsAppが必須なこともあって、他のSeleniumジョブと毛色が違うので、専用のJenkinsスレーブを立てた
    • 特にWindowsAppの仕様に引きづられて環境変数とか色々汚れてしまうので、テストNGの切り分けをシンプルにしたかった
  • SeleniumとWindowsNativeApplicationの連携はJenkinsジョブを分けて、成果物でやることにした
  • ジョブの連携は上流/下流で分けた
    • Selenium側のテスト結果を成果物にして、WindowsNativeApplication側テストのインプットにした
    • Selenium自体の不安定なテストNGが下流ジョブに影響しないようにした
      • 具体的にはSelenium系のExceptionが一度でも発生したら、ジョブの成果物を途中生成したのも含めて全部消した

最初にテストコードを書いてJenkinsジョブを作る時にこれらをやった記憶がある。
メンテナンスフリーにできるように、結構色々考えて設定したけど、実際に運用しはじめると色々出てきた....

のを↓に書いていきます

詰まったこと、上手く行かなかったこと

Seleniumのテストケースが増えてテストの実行時間が長くなってきた その1

Gradleの build.gradle に並列実行を設定した


test {
    maxParallelForks 10
}

これは上手く行かなかった。
並列実行分のSeleniumと操作対象のWebブラウザが起動するせいで、マシン負荷が増えて操作が遅くなりがちになり、タイムアウトが頻発して不安定になった。

最近わかったけど、並列実行時にWebDriverのインスタンス生成タイミングが重なると、ブラウザが3,4個以上同時に起動しようとして、ディスクI/Oが急激に増えて一気に重くなっていた。
(これは最近知ったので当時は意味不明だった...)

Seleniumのテストケースが増えてテストの実行時間が長くなってきた その2

それなら、と全体のwaitをリファクタリングしようと試みた。
waitが 1000ms とかで書かれてるところを WebDriverWait によるwaitに変更していった。

けど、対象に SinglePageApplication なWebページが含まれてて「何をもってwait完了か」の判定が難しいor煩雑なので、すべては直しきれなかった。

多少は全体の実行時間はよくなった。 けど、リファクタリングによる修正時間に見合うと思えるほどは良くならなかった...。

Seleniumのテストケースが増えてテストの実行時間が長くなってきた その3

Selenium実行用のJenkinsスレーブを1 -> 2台に増やした。

マシンリソースを増やすのは、やっぱり効果的で、概ね実行時間は半分になった。
これで一旦は解消した。
でも、スレーブ構築を自動化してなかったので構築が面倒だった。

あと色んなトラブルシューティングを両方にそれぞれ行う羽目になった。
今は大丈夫だけど、そのうち「片方のスレーブだけテストが動かない」とか起きる気がするので、失敗したなあ....と思う。

Selenium実行時にスレーブマシンのデスクトップを見てもブラウザが動いてないように見えた

でもJenkinsジョブは動いてた...
ジョブの実行結果はOKだったけど、NGになった時に調べるのが面倒くさそうだったので先に治しておくことにした。
※ Seleniumの実行状態を目視できる状態を作っておきたかった

よくよく調べるとJenkinsスレーブがサービスプロセス(SYSTEM権限)で動いていた。
この時に、WindowsはサービスモードだとGUIが出ない仕様だったのを思いだした。
http://wavetalker.blog134.fc2.com/blog-entry-77.html

Jenkinsスレーブの実行方式を Windowsサービス -> Java Web Start に変更して解消した。
※Windowsへのログオンユーザーの権限で起動するようにした

Seleniumテストが突然NGになる その1

ある日突然、妙にNGが起きるようになった。 SeleniumもJenkinsもテスト対象も、何も修正してないのに....

結局は...
テスト用のJenkinsスレーブはハイパーバイザ上のVMにしていたのだけど
同じハイパーバイザ上で大量のVMが展開されていて、ホスト側のマシンリソースを圧迫していた。
そのせいでVM側のJenkinsジョブの実行にも影響が起きていて、Webブラウザの操作がSelenium関係なくもっさりして不安定な状態だった。

具体的には、重すぎて各種Waitオブジェクトのデフォルトタイムアウトを超過してる時があった。
この手のやつは、この後もたまーに起きたけど、開発PCでは再現しないのが基本なので苦労する。

Seleniumテストが突然NGになる その2

気づいたらテスト対象の構造が変わってた。 よくあるやつ。
テスト対象のコードを知ってる部分も多かったので、そんなに困らなかった。

Seleniumテストが突然NGになる その3

WebBrowser or WebDriver が更新されていて動かなくなった。
比較的分かりやすいけど、回数が多くて面倒だった。
なのでWebDriverManagerをセットアップして自動化した。

まあ、自動更新したとしても、driverのバグが残ったままの状態の時とかあるけど....

完全には今も治ってないけど、だいぶマシにはなった。

Seleniumテストが突然NGになる その4

WebBrowser or WebDriver を更新した後、Firefoxだけ動かない。
でもWebDriverを起動した瞬間にエラーに襲われたり
なんか色々アレだったりするので、まさかリリースされているgeckodriverにそんなことあるはずなかろうと思ってかなりの時間試行錯誤した。
トラブった最初の一回だけ。

複数ブラウザでSeleniumテストしていて、firefoxというか、GeckoDriverだけ動かない時はまずGithub issueを見ましょう。

解消できたかどうかはお察しください。

ファイルアップロードが出来ない

普通のアップロードフォームなら、大したことはなかった。
でも、そうなんです....
みんなだいすきなオシャレwebサイトによくある、inputタグが隠蔽されたり、何故かdivタグだったりするやつです。

これはかなり困った。
作る方法はいくつかあったけど、とにかく安定しなかった。
結局、完全には解決出来ていないけど、試した方法を書きなぐっておきます。

ファイルパスを送信するパターン

単純にinputタグにhidden属性がついているだけなら、これで可能だったはず。

  • Sendkeysでローカルのファイルパスを指定してアップロードする
    • Seleniumはhidden状態のWebElementへのアクセスにはExceptionを発生させるので、基本的にはうまく出来ない
  • JavaScriptExecutorでローカルのファイルパスを指定してアップロードする
    • これで上手くいく場合は、これが一番簡単だと思う

ドラッグアンドドロップするパターン その1

何らかの要因で上が使えない場合とか、D&Dしたいんだ!って時は、こっちにせざるを得ない。
多少面倒だけど、「その2」に比べると天国...

JavaScriptExecutorを使って、Javascriptで次の処理を書いて実行すればいい。

1.ダミーのアップロード用のフォームをJavascriptで生成する
2.作ったフォームにアップロードパスを生成する
3.ドラッグ&ドロップ操作を行う

このコーディングを1から考えるのは頭が痛くなりそうだけど、同じようなことで困っている人がネット上にはたくさんいて
大体コピペすればなんとかなる。やったね!

こちらで詳細に書いてくれている方がいました。
https://qiita.com/himeyon/items/d6b74b6b6ffda6bed30b

ドラッグアンドドロップするパターン その2

その1が使えない場合、Web BrowserからWindowsの世界へ飛び立つ必要がある。
Windowsのデスクトップ上で置かれているファイルを何らかの方法でブラウザ上にアップロードする方法です。

どんな方法でも、かなり泥臭くなる。 
それに、Jenkinsジョブを作る時に「Windowsデスクトップの操作が発生するから云々...」とか考える必要があって、かなり沼る。
いくつかの方法に分けて書きます。

Java Robotを使う

意外と検索するとヒットするのがこの方法。 でもやめましょう。
コードは概ねコピペだし、大体のJava環境には入っているパッケージで出来て楽にできそうに見えます。
でもやめましょう。

なぜなら
 1.デスクトップのファイルの位置を、x,yの座標で指定して
 2.デスクトップからウェブラウザへのアップロードを
 3.x軸とy軸の座標移動を正確に行うコーディングで作る必要があるから
です

Jenkinsに持っていった瞬間動かなくなるのが目に見えています。
解像度とか最小化とか色んな要因で死にます。 魔境なのでやめましょう。

画像処理ライブラリを使う

OpenCVに代表されるような、画像認識ソフトを使います。

 1.デスクトップ上のファイルアイコンを画像認識してドラッグを開始し
 2.アップロード先の位置を画像認識してドロップする

多分、このような方法になるはずです。 「はず」というのは、このやり方は途中で作るのやめたので...
画像情報を予め作る必要があったので、それが結構面倒でした。

あと解像度の問題とかOS毎のアイコンの違いとかでうまく画像認識できずに動かなくなっちゃうのが想像できて、頓挫しました。

後から聞いた話だと、そのあたりをうまく吸収して自動化できているところもあるらしい。
是非知見を得たいところ。

Windowsのファイルアップロードダイアログを使うパターン

Windowsの世界に飛び出すパターンその2。 でもD&Dよりはマシだと思う。
フォームは隠蔽されてるけど、D&Dに拘らないならこの方法が使える。

Selenium側でファイルアップロードボタンを押すと、大体のサイトはファイルアップロードダイアログを出すと思うので、出した後OS操作でアップロードを行う。
方法は「Java Robotoで書く」とか「UWSCを使う」とか商用アプリ使うとか色々あるけど、個人的にはAutoItを使うのがオススメ。
https://www.autoitscript.com/site/autoit/

AutoItでアップロードする

独自言語なのでちょっと扱いにくいけど、SeleniumのSelectorと同じようにダイアログに対してIDを指定したり、アクティブや非アクティブのwaitを設定したりすることが出来るので、CI基盤に乗せた時に安定するコードを書きやすい。

詳細なコードはここのリンクが分かりやすい気がする。
http://toolsqa.com/selenium-webdriver/autoit-selenium-webdriver/

まとめ

ささっと思いつけたのはこのくらい。
まだもうちょっと困った気がする...

思い出したら追記していこうと思います。