Selenium+JenkinsのCIをできるだけコケなくするコツ

# はじめに
* ここでCIと言いましたが、夜間皆さんが寝ているときにせっせと働いてくれるアレを想定しています
* 私はJenkinsでSelenium+Capybaraのテストを毎晩まわすようなCI環境を構築しています
* ちなみにターゲットは以下のとおりで、対応するOS×ブラウザの数だけJenkinsジョブを用意しています
 * OS : Windows7, Windows8, Mac OS X 10.9, Linux CentOS
 * ブラウザ : IE10, IE11, Safari, Firefox, Chrome
* テストはCucumber+Selenium+Capybaraで作っています
* また個々に書いたこと以外でも思いついたら追記していきます

# できるだけコケなくするコツ

## 失敗してもへこたれるな、再実行じゃ！
* Jenkinsの機能としてもあるが、プラグインのほうが設定しやすかったり扱いやすいのでリトライ処理ができるプラグインを導入する
* Selenium使いは見たことがないとは言わせないたまに発生するTimeoutErrorでの失敗はこれで防げる
* 2つご紹介
* 1つ目はNaginator Plugin
 * ジョブが失敗したら再実行
 * 詳しくは[こちら](http://qiita.com/oh_rusty_nail/items/18ec73bee3f44c38823e)
 * 最大リトライ回数やインターバルを設定できる
 * 注意すべきはジョブの履歴を見ると失敗した痕跡(赤丸)が残ること
* 2つ目はBuild Flow Plugin
 * 名前のとおりいくつかのビルド(ジョブ)のフローを定義して1つのジョブとして扱えるプラグイン
 * ビルドフローとして扱いたい場合にはかなり使い勝手が良い
 * 詳しくは[こちら](http://qiita.com/oh_rusty_nail/items/a2addc8d0ca5a6531152)
 * 最大リトライ回数を設定できる
 * 最大リトライ回数内で成功すればジョブの履歴には失敗した痕跡が残らない(青丸)

## 「あきらめたらそこで試合終了だよ」。。。あきらめも肝心です！！
* 一定時間経過したら強制終了させるプラグインで対応可能
* 上記のリトライ処理させるプラグインと併用をおすすめ
 * Ex.) だいたい30分くらいのジョブなのに120分くらいがんばってる。。TimeoutErrorでてるかも？！
 * そんなとき、さっさとぶった切ってリトライしようーぜという考え
* [Build-timeout Plugin](https://wiki.jenkins-ci.org/display/JENKINS/Build-timeout+Plugin)
 * 数時間失敗してるにも関わらずジョブが走り続けるのを回避できる
 * そして、自分の環境ではjenkinsのバージョンが古いためかプラグインの利用可能リストに出てこずインストールできないというオチw
 * 諸事情によりバージョンアップができていないんですm(_ _)m

## テスト環境を確認しようず
* VM環境下でテストしているときにメモリやCPUを確認しておいたほうがいい
* もし、VMの割当もっとあてたいと思っても、マシンスペックに問題があるなら買い替えたりするところから始めないといけないので
* 失敗した事例をいくつか
 * Mac mini上にVM(Windows8用)×2を立てたらCPUが張り付いていました(これでテストするなど酷ですよね)
 * CentOS上にVMをぽこぽこ立てていて起動して触っていても問題なさそうに見えたが、テスト中で頑張っているところでCPUが張り付きJenkinsとのスレーブ接続が切れた

## テスト側の工夫

#### クリック後、別ウィンドウ/タブで開くようなリンク
* ブラウザによって挙動が変わったり、新規ウィンドウから元のウィンドウに戻れなかったりすることもある
 * 別ウィンドウ/タブで開くことが確認ポイントでないならば、以下のように書いた方が安全

```html
<html>
  <a href='https://www.google.co.jp' target='_blank'>Google Link</a>
</html>
```

```rb
# Before
find('a').click

# After
visit find('a')[:href]
```

#### 待機時間を必要に応じて挟む
* 時々ページ遷移やコンテンツの描画の速度などの原因により、押せるはずのボタンが押せない、出ているはずのダイアログが出ていないことがまたにある
* 対策としてはwait_for_page_to_loadのようなメソッドを定義して使うようにするとか素直にsleep入れるとか
* 事例としてはMac Safari環境下で実行するとclick後に一切の待機をしないということがあった
 * 対策として、個々のstepにsleep処理をいれるのは嫌なのでclickをオーバーライドした

#### タイトルチェック
* visitでのページ表示やclick後のページ遷移をしたつもりでもできていないこともあります
* タイトルチェックは適宜いれましょう

```rb
page.title.should == 'ホーム - Qiita'
```

#### 細かすぎる要素の指定はやめましょう
* コンテンツの構成や要素の順番などが少し変わっただけで通らなくなるような書き方は極力すべきではない
* idやclass,nameなどでDOM要素名と絡めるなどしてできるだけシンプルでユニークなセレクタにしたほが良い
* 良くない要素指定の例
 * 要素の順番や親子関係に依存しているもの
 * 例) ul li:nth-child(n) , h2 + div > div + span

#### 属性セレクタを有効活用
* コケなくするコツではないですがついでに
* スマホビューとPCビューでリンクのidが違うけど、1つのステップとして定義してしまいたい
* 属性セレクタを使えばこんな風に1ステップで書けちゃう

```html
<!-- For PC View -->
<a id='sample_service_login_pc'>サンプルサービスにログイン</a>

<!-- For SmartPhone View -->
<a id='sample_service_login_sp'>サンプルサービスにログイン</a>
```

```rb:sample_service_login_steps.rb
When /^サンプルサービスにログインする$/ do
  find("a[id^='sample_service_login']").click
end
```

## Jenkins側の工夫

#### 小さいジョブを作りましょう
* テストを作ったがテストを回すときにドバっと回すとテストシナリオが多いため、ものすごい時間がかかるってことも
 * 1シナリオ失敗で全部また回すとかしたくないので
 * 対象ブラウザ/テストシナリオのカテゴリなどでジョブを分割しましょう
 * そして作った小さなジョブ単位でリトライ処理も入れましょう
 * 本文書のタイトルにコケないと言いましたがコケてもすぐに立ち直ればいいんです！！

#### Jenkinsでの並行実行
 * スレーブを複数立てて並行実行しましょう
 * 並列実行を始めると当然その分だけ実行時間も短縮できます

----

# CIテスト環境構築などのメモ

## Mac Safari
* [SeleniumでSafariを動かす](http://qiita.com/oh_rusty_nail/items/488d6a0fa1684c809819)
* [「File exists - ~/Library/Safari/Extensions.bak」というエラーが出た時にすること](http://qiita.com/oh_rusty_nail/items/f5a870df65da0cfa2243)

## Mac Chrome
* [Mac Chromeでの自動テスト環境構築](http://qiita.com/oh_rusty_nail/items/7c84197bbbf27fc7b1c0)

## Mac Firefox
* [Seleniumでwaiting for evaluate.js load failedが出たときの対応](http://qiita.com/oh_rusty_nail/items/ac1b40afbafc4fe91424)

## スマホビュー
* [Seleniumでスマホビューをテストする](http://qiita.com/oh_rusty_nail/items/2dacfc22f06f9669cba5)

## その他
* [Seleniumでクリック操作後に待機させる対応](http://qiita.com/oh_rusty_nail/items/c589f1bfc8563b5c9e76)
* [Cucumberでテストを書くときのポイント](http://qiita.com/oh_rusty_nail/items/940c5b89f0f95c5fa2ad)
* [[覚え書き] CSS再入門～セレクタ～](http://qiita.com/oh_rusty_nail/items/e522fe74bc3547b3e826)
* [Seleniumでwaiting for evaluate.js load failedが出たときの対応](http://qiita.com/oh_rusty_nail/items/ac1b40afbafc4fe91424)

※ Firefoxはデフォルトで使えるし、IE, ChromeはIEDriverとChromeDriverを入れてやれば動く

# ～ただの宣伝～
* [全国のSeleniumer必読](http://qiita.com/oh_rusty_nail/items/b8ba525d31ea7c522856)
* Selenium, SauceLabs, Travis, Jenkinsに関するノウハウ書いているのでよかったら参考にしてみてください