Selenium Grid(Docker) + Turnip + RSpec を使って自動テストを実行した手順(備忘録)

## そもそもSelenium Grid って？

一つのテストスクリプトで複数の実機のブラウザで自動にテストができるサーバのこと。
Selenium Grid Server(hubという)を自動テストで使うWebdriverの接続先にすることで以下のようなメリットが得られる。

* ローカル環境でブラウザとWebdriverの準備が不要
* プラットフォーム、ブラウザを指定するだけで、その環境のE2Eテストが実行できる
* 自動テストの並列化が簡単にできる
* ローカルでブラウザを実行しないので、自動テストと開発を同時に進めることができる

## 本備忘録のゴールと内容

### ゴール

`selenium Grid Server` を立てて、 `chrome` ブラウザで `google` に接続して 「長谷川豊」で検索を行い、その検索結果のスクリーンショットを取得するテストを実行する

### 内容

* Selenium Grid Server(以下hub)の環境構築
    * Docker を使用する
* hub に接続する node-chorome(実際に自動テストを実行する環境)の構築
    * Docker を使用する
* rspec + turnip を利用したテストコードの作成

### お急ぎ便

* 以下のリポジトリに上記のゴールと内容を試せるスクリプトを配置しています
    * https://github.com/toririn/selenium_hub
        * Docker, Ruby が使えるのが前提条件です。詳しくは README を参照ください。

## 備忘録開始

### Selenium Grid の環境構築

Docker hub に Selenium Grid のイメージが用意されているので取得します。

念のため version を合わせたいので現時点(2016年10月02日)の最新である `2.53.1` のイメージを取得します。

* [selenum/hub](https://hub.docker.com/r/selenium/hub/)

```
docker pull selenium/hub:2.53.1
```

コンテナ化します。

```
docker run -d -P --name selenium-hub selenium/hub:2.53.1
```

hubが接続を待っているポートは 4444 ポートです。今回はポートフォワディングを自動でやっていますが、
もし hub を接続するポート番号を自分で設定したかったら以下のようにしましょう。

```
docker run -d -p "14444:4444" --name selenium-hub selenium/hub:2.53.1
```

続いて実際に chrome で自動テストを行ってくれるイメージ(以下node)の環境構築です。
これも Docker Hub にイメージがあるのでそのまま取得しましょう

* [selenium/node-chrome-debug](https://hub.docker.com/r/selenium/node-chrome-debug/)
    * -debug と suffix が付いているものは VNC サーバが内蔵されているものです。
    * こちらのイメージを使ってコンテナに vnc で接続すれば自動テストが回っているところを見ることができます
    * 今回はこのイメージを使います

```
docker pull selenium/node-chrome-debug:2.53.1
```

コンテ化します

```
docker run -d -P --name sekeniun-chrome --link selenium-hub:hub selenium/node-chrome-debug:2.53.1
```

ここで重要なのは `--link` オプションで `selenium-hub` イメージとリンクを張ることです。

さて、ここで Selenium Grid の環境構築がうまくできたか確認をしてみましょう。

```
docker ps
```

で以下のようにコンテナが起動されているか確認しましょう。（詳細な値は別にして2つコンテナが起動できていればOKです)

```
CONTAINER ID        IMAGE                               COMMAND                  CREATED             STATUS              PORTS                     NAMES
651000000000        selenium/node-chrome-debug:2.53.1   "/opt/bin/entry_point"   2 minutes ago       Up 2 minutes        0.0.0.0:32772->5900/tcp   sekeniun-chrome
881000000000        selenium/hub:2.53.1                 "/opt/bin/entry_point"   2 minutes ago       Up 2 minutes        0.0.0.0:32768->4444/tcp   selenium-hub
```

続いてブラウザで `hub` に接続してみましょう。

まず `hub` の 4444　ポートにポートホワディングされているポートを取得します

```
docker port selenium-hub 4444
#=> 0.0.0.0:32768
```

こうした結果が得られたならば 32678 が selenium-hub の 4444 ポートにホワディングされている値になります。

ではブラウザで以下の URL にアクセスしましょう。

* http://localhost:32768/grid/console
    * 32678 や localhost のところは適宜自分の環境に合わせて書き換えてください。

うまく環境構築ができれば以下のような画面へ遷移するかと思います。

![grid.jpg](https://qiita-image-store.s3.amazonaws.com/0/101975/1193c338-8cff-a554-d34a-169d1eb08c52.jpeg)


以上で `Selenium Grid` 環境構築は終了です。

## テストスクリプトの環境構築

rspec と turnip のテストを書きましょう。まずは環境を用意します。

お好きなディレクトリにまず移動してください。今回は ~/work/sample ディレクトリで作業をすることを前提に進めます。

```
mkdir -p ~/work/sample
cd $_
```

Gemfileを作成して以下のgemをインストールしましょう。

```
bundle init
```

```ruby:Gemfile
source "https://rubygems.org"
 
gem 'rspec'
gem 'turnip'
gem 'capybara'
gem 'selenium-webdriver'
```

```
bundle
```

続いてrpsecを初期化しましょう。
ついでに先にrspecにturnipを読み込む設定も追加しておきましょう。

```
bundle exec rspec --init
```

```ruby:.rspec
--color                                                                                                                                                                                                        
--require spec_helper
-r turnip/rspec
```

下記を参考に spec/spec_helper を編集します。

* [RailsじゃないRspec3環境を構築する方法](http://qiita.com/yusabana/items/db44b81bdddf6ed0e9f5)
    * spec/spec_helper.rb の 52行目あたりと102行目あたりの `=begin` と `=end` を消せばいいです。

次に turnip を使うため turnip_helper.rb を作成します。


```ruby:turnip_helper.rb
require 'turnip'
require 'turnip/capybara'
require 'turnip/rspec'
require 'capybara'
 
#driverの設定
Capybara.register_driver :selenium do |app|
  Capybara::Selenium::Driver.new(app, :browser => :chrome, :url => "http://localhost:#{ENV['SELENIUM_HUB_PORT']}/wd/hub")
end
 
Capybara.configure do |config|
  config.default_driver = :selenium
  config.javascript_driver = :selenium
  config.ignore_hidden_elements = true
  config.default_wait_time = 30
end
 
Dir.glob("spec/steps/**/*steps.rb") { |f| load f, true }
```

最後に steps と feature のテストコードを置くディレクトリを作成しましょう。

```
mkdir -p spec/steps/tests
mkdir -p spec/features/tests
```

これで環境は整いました。

## テストスクリプトを作成する

では肝心の `googleに接続して「長谷川豊」で検索してスクリーンショットを得る` テストコードを書いていきましょう。

まずは steps を用意します。spec/steps/tests/test_steps.rb を作成しましょう。

```ruby:spec/steps/test_steps.rb
# encoding: utf-8                                                                                                                                                                                                  

steps_for :common do
  step 'googleを訪問' do
    url = "http://www.google.com"
    visit url 
    page.save_screenshot 'screenshot.png'
  end 

  step 'googleのページが表示されている' do
    expect(page).to have_content("Google.com")
    page.save_screenshot 'spec/google.png'
  end 

  step '「長谷川豊」で検索する' do
    within("form") do
      fill_in "q", with: "長谷川豊"
    end 
    click_on "検索"
  end 

  step '検索結果のページが表示されている' do
    expect(page).to have_content("長谷川豊")
    page.save_screenshot 'spec/search_result.png'
  end 
end

```

続いて feature を作成します。 spec/features/tests/test.feature を作成してください。

```spec/features/test.feature
# encoding: utf-8
# language: ja
@common
機能: サンプル
  シナリオ: googleで検索ができること
    もし googleを訪問
    ならば googleのページが表示されている
    もし 「長谷川豊」で検索する
    ならば 検索結果のページが表示されている                                                                                                                                                                                                                       
```

これでテストスクリプトは完成です。

## テストを実行する。


それではテストを実行しましょう。
まずテストスクリプト内にも記載をした下記のために環境変数を設定します。

```ruby:turnip_helper.rb
#driverの設定
Capybara.register_driver :selenium do |app|
  Capybara::Selenium::Driver.new(app, :browser => :chrome, :url => "http://localhost:#{ENV['SELENIUM_HUB_PORT']}/wd/hub")
end
```

以下のコマンドで取得できるポートを環境変数 `SELENIUM_HUB_PORT` へ入れてください。

```bash
docker port selenium-hub 4444
#=> 0.0.0.0:32768
export SELENIUM_HUB_PORT=32768
```

これでテストが実行できます。実行してみましょう。

```
bundle exec rspec
```

※Selenium Grid へ接続するのに 5秒ほどかかります。
※その後テスト結果が出力されます。

ちなみに成功すると下記のようなログが吐き出されます。

```bash
サンプル
  googleで検索ができること
    もしgoogleを訪問 -> ならばgoogleのページが表示されている -> もし「長谷川豊」で検索する -> ならば検索結果のページが表示されている

Top 1 slowest examples (12.96 seconds, 100.0% of total time):
  サンプル googleで検索ができること もしgoogleを訪問 -> ならばgoogleのページが表示されている -> もし「長谷川豊」で検索する -> ならば検索結果のページが表示されている
    12.96 seconds ./spec/features/tests/test.feature:5

Finished in 12.96 seconds (files took 0.5102 seconds to load)
1 example, 0 failures
```

これでひとまず実行できる環境は出来ました。

続いて応用編として fig を使ったnodeの並列化とかを書いていこうと思います。

## 参考

* http://www.techscore.com/blog/2015/05/10/selenium-grid/
* http://qiita.com/derui@github/items/3dfb5b11c06b28579841
    * 参考というかほぼここの内容です

## 補足

* node を増やすと hub が自動的に自動テスト先を割り振ってくれるみたいですね。
* node も今はDockerで構築しているため Linux のプラットフォームばかりですが、Windows や Mac　に　node サーバを構築して　hub　に接続すれば自動テスト先として使えるみたいです。
* 何より Hub を一つたてておけば、ほかのどの環境からも E2E テストが出来るのは強みですね。