RSpec x Capybara x Webkit x X Server で JavaScript の絡むテストの自動化

RSpec x Capybara で画面のテストをしていたんだが、JavaScript を叩いてページ遷移している箇所があり、こいつのテストをするのに手間取ってしまった。曰く、

- [Capybara-Webkit](https://github.com/thoughtbot/capybara-webkit) を使えば JavaScript を絡めた小粋なテストができる
- [Qt](https://github.com/thoughtbot/capybara-webkit/wiki/Installing-Qt-and-compiling-capybara-webkit#ubuntu-1404-trusty-vagrant-ubuntutrusty64) なるものをインストールしないと Capybara-Webkit はコンパイルできないらしい。
- [Xvfb](https://en.wikipedia.org/wiki/Xvfb) が無いと Capybara-Webkit はwebkit_server を立ちあげられないらしい（こいつを調べるのにくっそ時間がかかった）

まだ完全には理解できていないがひとまず動くようになったので何をやったかをメモしておく。

# 環境
- OS
    - `Ubuntu 14.04LTS`
    - `vagrant` の `ubuntu-14.04-amd64` という box を使っている。
- Ruby
    - 2.2.3
- Rails
    - 4.2.4
- RSpec
    - 3.3.0
- Capybara
    - 2.5.0
- Capybara-Webkit
    - 1.7.1
- qmake
    - 4.8.6
    - テストを走らせると `WARNING: The next major version of capybara-webkit will require at least version 5.0 of Qt. You're using version 4.8.6.` と出るんだが qt5 のインストール方法は未調査
- xvfb
    - 2:1.15.1-0ubuntu2.7

# やったこと順
- [capybara-webkit](https://github.com/thoughtbot/capybara-webkit) をインストール

```rb:Gemfile
group :test do
  # ...
  gem "capybara-webkit"
  # ...
end
```

```
$ bundle install --path vendor/bundle --without=production --binstubs=bundle_bin
```

- 失敗。よくよく README を見ると [qt をインストールしろ](https://github.com/thoughtbot/capybara-webkit#qt-dependency-and-installation-issues)と書いてある
- qt インストール。

```
$ sudo apt-get install -y libqtwebkit-dev qt4-qmake
```

- 再び bundle インストール。今度はうまくいく
- テスト実行

```
$ rspec
```

```
...
Capybara::Webkit::ConnectionError:
... capybara-webkit-1.7.1/bin/webkit_server failed to start.
```

- 失敗。[ググっても](https://github.com/thoughtbot/capybara-webkit/issues/678)大した情報は得られず。
- 仕方ないのでソースを読む
- `ConnectionError` を出しているのはどうやら[ここ](https://github.com/thoughtbot/capybara-webkit/blob/v1.7.1/lib/capybara/webkit/connection.rb#L87)。
- `parse_port` は [`discover_port`](https://github.com/thoughtbot/capybara-webkit/blob/v1.7.1/lib/capybara/webkit/connection.rb#L91) から呼び出されており、
- `discover_port` は [`start_server`](https://github.com/thoughtbot/capybara-webkit/blob/v1.7.1/lib/capybara/webkit/connection.rb#L72) から呼びだされている。
- `start_server` では、はじめに [`open_pipe`](https://github.com/thoughtbot/capybara-webkit/blob/v1.7.1/lib/capybara/webkit/connection.rb#L79) が呼び出され、 `SERVER_PATH` を `Open3.popen3` とやらでオープンしようとしている。
- どうもこいつが何かのオープンに失敗しており、`@pipe_stdout.first` が `nil` を返すために、[文字列判定](https://github.com/thoughtbot/capybara-webkit/blob/v1.7.1/lib/capybara/webkit/connection.rb#L84) に失敗してエラーが投げられる模様
- [`SERVER_PATH`](https://github.com/thoughtbot/capybara-webkit/blob/v1.7.1/lib/capybara/webkit/connection.rb#L8) が指す `webkit_server` というのが実行可能なファイルのようなので、実行してみる。すると以下のエラーが。

```
$ vendor/bundle/ruby/2.2.0/gems/capybara-webkit-1.7.1/bin/webkit_server
webkit_server: cannot connect to X server
```

- エラーでググッて[ここ](https://github.com/thoughtbot/capybara-webkit/issues/279)にたどり着く。
- あー xvfb てのが必要なのねってことでインストール

```
$ sudo apt-get install -y xvfb
```

- インストール後下記を実行

```
$ Xvfb :1 -screen 0 1024x768x16 -nolisten inet6
```

- すると、X Server が立ち上がり臨戦態勢に。こいつにプロセス奪われてたんじゃテストできないじゃん
- 仕方ないので別タブで開発サーバーに入り、そっちで起動する。
- テスト用タブでは下記を実行し、テスト

```
$ export DISPLAY=localhost:1.0
$ rspec
```

- これでようやく動く

# まとめると
- xvfb インストール

```
$ sudo apt-get install -y xvfb

```

- qt インストール

```
$ sudo apt-get install -y libqtwebkit-dev qt4-qmake
```

- capybara-webkit インストール

```rb:Gemfile
group :test do
  # ...
  gem "capybara-webkit"
  # ...
end
```

```
$ bundle install --path vendor/bundle --without=production --binstubs=bundle_bin
```

- xvfb 起動

```
$ Xvfb :1 -screen 0 1024x768x16 -nolisten inet6
```

- DISPLAY を指定してからテスト実行

```
$ export DISPLAY=localhost:1.0
$ rspec
```

# 自動化
rspec 使うたび、あるいは開発環境起動するたびに Xvfbも立ち上げるのは面倒くさいので自動化したい。そこで、テストのはじめに別プロセスで立ち上げ、テストの終わりに `killall` で葬り去ることにした。

`spec/rails-helper.rb` に下記の通り追記

```rb:spec/rails-helper.rb
config.before :suite do
  system "Xvfb :1 -screen 0 1024x768x16 -nolisten inet6 &"
end

config.after :suite do
  system "killall Xvfb"
end
```

環境変数 `DISPLAY` は、`.env` ファイルに追記（多分 [dotenv](https://github.com/bkeepers/dotenv) という gem が必要）

```:.env
DISPLAY="localhost:1.0"
```

これで、 `RSpec` を叩くだけでテストが動く。