Capybaraチートシート

当文書はRails + Rspec + Capybara環境を想定しています。
他のアプリ/テストフレームワークを利用したい場合は[公式のREADME](https://github.com/teamcapybara/capybara)を参照願います。


概要
------------------------------------------------------------

Capybara = UIテストのためのrubyフレームワーク

- オープンソース / MITライセンス
- ページの表示、フォームの入力、ボタンのクリック等のUI操作をテストコード上で実行できる

- 複数のテストフレームワークから利用できる
    - MiniTest, RSpec, Cucumberなど

- 複数の __ドライバ__ (テスト環境, ヘッドレスブラウザ)を選択して利用できる
    - Rack::Test, Selenium, Webkitなど


導入
------------------------------------------------------------

### インストール

gemをインストールする。
Railsの場合、バージョンによってはデフォルトでGemfileに追加されている。

```diff:Gemfile
group :development, :test do
+  # Capybara本体
+  gem 'capybara'
+
+  # Capybaraが利用するドライバを任意に追加する
+  gem 'selenium-webdriver'
+  gem 'capybara-webkit'
end
```

```bash
$ bundle install
```

RSpecで利用する場合は、スペックのヘルパファイルに記述を追加する。

```diff:spec/spec_helper.rb
+ require 'capybara/rspec'
```


### ドライバの選択
Capybaraはテストを実行する環境(ヘッドレスブラウザ)を選択できる。

ドライバの種類  | Capybaraにおけるシンボル  | 概要
---             | ---                       | ---
Rack::Test      | `:rack_test`              | デフォルト。 Rails標準のテスト機能。Rackと通話して通信をシミュレートする。高速だがJavaScriptを使えない、Rackアプリ外の通信をテストできない。
Selenium        | `:selenium`               | js利用時のデフォルト。Selenium Web DriverによりFirefoxをヘッドレスブラウザとして起動し、その中でテストする。多機能だがFirefoxに依存する。
Webkit          | `:webkit`                 | QtWebkitを使ったヘッドレスブラウザ環境。Webkit準拠、Seleniumより高速。
Poltergeist     | `:poltergeist`            | PhantomJSベースのヘッドレスブラウザ環境。ピュアヘッドレス(のため高速)、Webkit準拠。


ドライバは、Capybara全体のデフォルト設定として変更することができる:

```rb:spec/spec_helper.rbなど
# ドライバを設定(デフォルトは:rack_test)
Capybara.default_driver = :rack_test

# jsオプション有効時のドライバを設定(デフォルトは:selenium)
Capybara.javascript_driver = :selenium
```

#### RSpecの場合

通常はデフォルトのドライバ(`default_driver`)が使われる。
RSpecにおいてJavaScriptシミュレーションを使いたいスペック/検査は、`js: true`を指定することで`javascript_driver`が使われる。

```rb:some_spec.rb
describe '...', js: true do
  # javascriptが有効になる
end
```

また、スペック/検査ごとに明示的にドライバを指定して差し替えることもできる。

```rb:some_spec.rb
describe '...', driver: :selenium
  # このdescribe内に限り、Seleniumが使われる
end
```

スペック内で一時的にドライバを差し替えることも可。

```rb:some_spec.rb
# 一時的にドライバを変更
Capybara.current_driver = :selenium

# ...

# 元に戻す
Capybara.use_default_driver
```


DSL
------------------------------------------------------------

RSpecにおいてCapybaraはビュースペック `spec/views/*` あるいはフィーチャースペック `spec/features/*` において利用する。
Capybaraの機能は、RSpecと同じくテストを記述的に実装するためにDSLとして提供されている。

機能の全容は[公式のRDoc](http://www.rubydoc.info/github/teamcapybara/capybara/master)を参照のこと。
以下、主要な機能のみ抜粋する。

### locator

Capybaraにおける要素の指定は、`locator`文字列によって操作対象の要素を指定する。

`locator`には以下のような文字列を指定できる:

- 対象の要素のid属性の値
- 対象の要素のname属性の値
- 対象の要素のinner HTML の値
- 対象の要素に対応する`<label>`の値(inner HTML)


たとえば以下のビューにあるリンクをテストしたい場合、

```erb:app/views/demo/index.html.erb
<a href="path/to/foo" id="id-link-foo">Link Foo</a>
```

以下のように複数の方法で対象を指定できる。

```rb:spec/features/demo_spec.rb
# IDで指定する
click_link 'id-link-foo'

# inner HTMLで指定する
click_link 'Link Foo'
```

メソッドによっては、HTMLタグ名、CSSセレクタ、XPathなどによって指定することもできる。
詳細はメソッドごとの公式のリファレンスを参照のこと。


### ページにアクセスする(Capybara::Session)

#### visit
`visit`でページへアクセスする(GETリクエスト)。

```rb
visit 'path/to/target'
```

`visit`でページアクセスすると、`page`変数に格納される`Capybara::Session`オブジェクトが更新される。
後述のAction, Matcher, Finderなどは __暗黙的に__ このページオブジェクトに対して操作を実行する。


### 要素を操作する(Capybara::Node)

Capybaraは要素を検索し、操作したり内容を検証したりできる。
各メソッドは要素オブジェクト(`Capybara::Node`)に対するインスタンスメソッドとして動作するが、
省略した場合(Capybaraのクラスメソッドとして記述した場合)は`visit`で取得したページ`page`に対して適用される。


#### 要素を検索する(Capybara::Node::Finder)

Finderを使うことで、`visit`で得られたページに含まれる特定の要素を検索・操作できる。

メソッド                     | 動作
---                         | ---
`find('locator')`           | 要素を検索
`all('tag')`                | HTMLタグにより要素を検索し、全件取得
`all(:css, 'selector')`     | cssセレクタにより要素を検索し、全件取得
`all(:xpath, 'selector')`   | XPathにより要素を検索し、全件取得
`first(:css, 'locator')`    | 最初に見つかった要素を取得
`find_button('locator')`    | ボタンを検索
`find_by_id('id')`          | IDにより検索
`find_field('locator')`     | テキストフィールドを検索
`find_link('locator')`      | リンクタグを検索


#### 要素の状態を確認する(Capybara::Node::Element)

Finderにより取得した要素(`Element`)は、その状態を確認できる。

メソッド                    | 動作
---                         | ---
`[:foo]`                    | 属性fooの値を取得
`checked?`                  | チェック状態を取得
`disabled?`                 | 無効状態を取得
`multiple?`                 | multiple属性を取得
`path`                      | XPathを取得
`readonly?`                 | readonly属性を取得
`selected?`                 | プルダウンの選択状態を取得
`tag_name`                  | タグ名を取得
`text`                      | innerHTMLを取得
`value`                     | value属性を取得
`visible?`                  | 可視状態を取得


また要素を操作できる。

メソッド                    | 動作
---                         | ---
`click`                     | クリックを実行
`double_click`              | ダブルクリックを実行
`drag_to(elem)`             | 別の要素にドラッグする
`hover`                     | hover状態にする
`right_click`               | 右クリックする
`select_option`             | プルダウンを選択
`send_keys('text')`         | キー入力
`set('text')`               | フォーム要素に対してテキストを入力
`trigger('click')`          | イベントをトリガする
`unselect_option`           | プルダウンの選択状態を解除


### UIを操作する(Capybara::Node::Actions)

Elementのメソッドの他、`Capybara::Node::Actions` のメソッドでもUIの操作が可能。

メソッド                                         | 動作
---                                              | ---
`click_button('locator')`                        | ボタンをクリックする
`click_link('locator')`                          | リンクをクリックする
`click_on('locator')`                            | ボタンあるいはリンクをクリックする(`click_link_or_button`のエイリアス)
`fill_in('locator', with: 'text')`               | テキストボックスに入力する
`check('locator')`                               | チェックボックスをチェックする
`uncheck('locator')`                             | チェックボックスのチェックを外す
`select('option value' from: 'select locator')`  | セレクトボックスを選択状態にする
`unselect('option value' from: 'select locator')`| セレクトボックスを非選択状態にする
`choose('locator')`                              | ラジオボタンを選択する
`attach_file('locator', 'path/to/file')`         | ファイルセレクタにファイルを設定する


#### 要素の内容を確認する(Capybara::Node::Matcher)

UIの存在確認のための検索メソッドは、`Capybara::Node::Matchers`に含まれる。
詳細は[公式のRDoc](http://www.rubydoc.info/github/teamcapybara/capybara/master/Capybara/Node/Matchers)を参照のこと。

matcherは、`Element`あるいは`Session`(page)オブジェクトに対してインスタンスメソッドのかたちで実行できる。

```rb
page.has_css?('.target_class')
```

あるいは、RSpecのMatcherとしても書ける。

```rb
expect(page).to have_css('.target_class')
```


メソッド                                        | 動作
---                                             | ---
`has_button?('locator')`                        | ボタンの存在確認
`has_checked_field?('locator')`                 | チェック状態のチェックボックスの存在確認
`has_css?('selector')`                          | CSS定義の存在確認
`has_field?('locator')`                         | フィールドの存在確認
`has_link?('locator')`                          | リンクの存在確認
`has_select?('locator')`                        | プルダウンの存在確認
`has_selector?('selector')`                     | セレクタの存在確認
`has_table?('locator')`                         | テーブルの存在確認
`has_text?('text')`                             | 文言の存在確認
`has_unchecked_field?('locator')`               | チェックされていないラジオボタンの存在確認
`has_xpath?('path')                             | XPathの存在確認
`has_no_button?('locator')`                     | ボタンの存在確認(否定)
`has_no_checked_field?('locator')`              | チェック状態のチェックボックスの存在確認(否定)
`has_no_css?('selector')`                       | CSS定義の存在確認(否定)
`has_no_field?('locator')`                      | フィールドの存在確認(否定)
`has_no_link?('locator')`                       | リンクの存在確認(否定)
`has_no_select?('locator')`                     | プルダウンの存在確認(否定)
`has_no_selector?('selector')`                  | セレクタの存在確認(否定)
`has_no_table?('locator')`                      | テーブルの存在確認(否定)
`has_no_text?('text')`                          | 文言の存在確認(否定)
`has_no_unchecked_field?('locator')`            | チェックされていないラジオボタンの存在確認(否定)
`has_no_xpath?('path')                          | XPathの存在確認(否定)
`matches_css?('locator')`                       | 対象が指定のCSS定義に該当する
`matches_selector?('selector')`                 | 対象が指定のセレクタに該当する
`matches_xpath?('xpath')`                       | 対象が指定のXPathに該当する
`not_matches_css?('locator')`                   | 対象が指定のCSS定義に該当する
`not_matches_selector?('selector')`             | 対象が指定のセレクタに該当する
`not_matches_xpath?('xpath')`                   | 対象が指定のXPathに該当する


アサーションメソッドも存在する。


メソッド                                        | 動作
---                                             | ---
`assert_all_of_selectors(:css, 'selector'...)`  | 指定のセレクタがすべて対象内に存在することを確認
`assert_matches_selector('selector'...)`        | 対象が指定のすべてのセレクタに合致することを確認
`assert_selector('selector')`                   | 対象かその子孫が指定したセレクタに合致することを確認
`assert_text('text'...)`                        | 対象のinnerHTMLが指定のテキストを含むこと
`assert_none_of_selectors(:css, 'selector'...)` | 対象が指定のすべてのセレクタに合致することを確認(否定)
`assert_not_matches_selector('selector'...)`    | 対象が指定のすべてのセレクタに合致することを確認(否定)
`assert_no_selector('selector')`                | 対象かその子孫が指定したセレクタに合致することを確認(否定)
`assert_no_text('text'...)`                     | 対象のinnerHTMLが指定のテキストを含むこと(否定)


### スコープを切る(within)

`within`を使うと、検索/操作の影響範囲を制限することができる。

```
within(:css, '.target_selector') do
  # 指定したセレクタ内に対してのみ検索/操作が行われる
end
```

メソッド                        | 動作
---                             | ---
`within('selector')`            | 指定セレクタに該当する要素以下をスコープとする
`within(node)`                  | 指定ノード(`Element`)以下をスコープとする
`within_fieldset('locator')`    | 指定のフィールド以下をスコープとする
`within_table('locator')`        | 指定のテーブル以下をスコープとする


### ウィンドウを制御する(window)

複数のウィンドウを制御することができる。

メソッド                        | 動作
---                             | ---
`windows`                       | ウィンドウの一覧取得
`switch_to_window(window)`      | ウィンドウを選択
`window_opened_by({})`          | ウィンドウを開く(windowオブジェクトを返す)
`window_opened_by do ... end`   | ウィンドウを開く(そのまま操作実行)
`within_window(window)`         | 指定のウィンドウをスコープとする
`window.close`                  | ウィンドウを閉じる
`window.closed?`                | ウィンドウが閉じられたか確認
`window.current?`               | ウィンドウがカレント状態か確認
`window.resize_to(w, h)`        | ウィンドウサイズを変える
`window.maximize`               | ウィンドウを最大化する
`window.size`                   | ウィンドウサイズを返す


### JavaScriptを実行する(execute_script)

ページ内で任意のJavaScriptを実行できる。

```ruby
execute_script("'some javascript code'")
```

評価をruby側に返すこともできる。

```ruby
ret = evaluate_script("'some javascript code'")
```


また、Ajaxなどの非同期処理を行う場合、Capybaraはこれを自動的に処理する。
非同期処理のための待ち時間は任意に設定できる。

```rb
Capybara.default_max_wait_time = 5
```


### モーダル表示を制御する

アラート表示を操作できる。

メソッド                        | 動作
---                             | ---
`accept_alert do ... end`       | アラートを補足し、ブロック内で操作する(通常はボタンクリックする)
`dismiss_confirm do ... end`    | 確認アラートを補足し、ブロック内で操作する(通常はボタンクリックする)


### デバッグする

デバッグのために、ページの情報を取得できる。

メソッド                                    | 動作
---                                         | ---
`page.status_code`                          | ステータスコードを取得する
`page.html`                                 | そのページのDOMを文字列として取得できる。
`page.save_screenshot('screenshot.png')`    | スクリーンショットを取得する


### 他のアプリ/サーバに接続する

ローカルのRails(Rack App)のほか、任意のサーバに接続できる。

```rb
Capybara.app_host = 'http://www.google.com'


# ローカルのRackサーバを使わないことを明示できる
Capybara.run_server = false
```

参考
------------------------------------------------------------
- [Github - teamcapybara/capybara](https://github.com/teamcapybara/capybara)
- [RubyDoc - Capybara](http://www.rubydoc.info/github/teamcapybara/capybara/master)
    - [Capybara::Node::Actions](http://www.rubydoc.info/github/teamcapybara/capybara/master/Capybara/Node/Actions)