RSpec入門　6.システムスペック

[前回](https://qiita.com/zeisho/items/344d913d02063660bfe6)の続き

#システムスペックとは
テストには、コントローラスペックやモデルスペックのような、単一のモデル、コントローラが期待通りに動作するかチェックする**単体テスト**と、複数のモデル、コントローラを統合したプログラム全体の挙動をチェックする**統合テスト**があります。

RSpecでは、この統合テストの一つとして**システムスペック**が提供されています。（似たテストでフィーチャースペックもありますが、Rails5.1以降では非推奨になりました。）

システムスペックは、統合テストの中でも、特にUIのテストとして有効です。

#準備
ブラウザの操作をシュミレートするために**Capybara**をインストールします。これを使うと、テスト内でリンクをクリックしたり、画面の表示を検証したりできます。

##Capybaraのインストール
Rails5.0以降では、Capybaraはデフォルトでインストールされているので、もしインストールされていない場合はGemfileからインストールしましょう。

```Gemfile
group :test do
  gem 'capybara'
end
```

```
$ bundle install
```

##設定
テストでシステムスペックとCapybaraを使えるようにするために、設定に追記します。

```spec/rails_helper.rb
#追記
require 'capybara/rspec'

RSpec.configre do |config|
  #追記
  config.before(:each, type: :system) do
    driven_by :rack_test
  end

  config.before(:each, type: :system, js: true) do
    driven_by :selenium_chrome_headless
  end
```

#システムスペックの基本
準備が終わったので、まずはスペックファイルを作っていきましょう。

```
$ rails g rspec:system projects
```

Capybaraではvisitやfill_in、click_linkといったメソッドが提供されており、アプリで必要な機能のシナリオを簡単に書くことができます。

```spec/features/projects_spec.rb
require 'rails_helper'

RSpec.describe "Projects", type: :system do
  scenario "user creates a new project" do  #itと同じexampleの起点
    user = FactoryBot.create(:user)

    #ログイン操作
    visit root_path
    click_link "Sign in"
    fill_in "Email", with: user.email
    fill_in "Password", with: user.password
    click_button "Log in"

    #プロジェクト作成
    expect {
      click_link "New Project"
      fill_in "Name", with: "Test Project"
      fill_in "Description", with: "Trying out Capybara"
      click_button "Create Project"
      expect(page).to have_content "Project was successfully created"
      expect(page).to have_content "Test Project"
      expect(page).to have_content "Owner: #{user.name}"
    }.to change(user.projects, :count).by(1)
  end
end
```

システムスペックでは、ユーザーがブラウザ上で行う操作に近いテストができます。

#Capybaraのメソッド
上記のスペック以外にも、Capybaraは多くのメソッドを提供しています。
その中でも頻出のものを紹介します。

**ページを開く**

```
visit "/fake/page"
```

**リンクまたはボタンのラベルをクリックする**

```
click_on "A link or button label"
```

**チェックボックスのラベルをチェックする**

```
check "A checkbox label"
```

**チェックボックスのラベルのチェックを外す**

```
uncheck "A checkbox label"
```

**ラジオボタンのラベルを選択する**

```
choose "A radio button label"
```

**セレクトメニューからオプションを選択する**

```
select "An option", from: "A select menu"
```

**ファイルアップロードのラベルでファイルを添付する**

```
attach_file "A file upload label", "/some/file/in/my/test/suite.f"
```

**指定した CSS に一致する要素が存在することを検証する**

```
expect(page).to have_css "h2#subheading"
```

**指定したセレクタに一致する要素が存在することを検証する**

```
expect(page).to have_selector "ul li"
```

**現在のパスが指定されたパスであることを検証する**

```
expect(page).to have_current_path "/projects/new"
```

##曖昧さ回避
ページ内に指定したセレクタが複数該当してCapybaraに「曖昧ですよ」と怒られる場合は、スコープを制限することで解決できます。

```rb
#指定したclassかidを持つリンクテキストをクリック
within "#id or .class" do
  click_link "click here!"
end

#指定したclassかidを持つタグをクリック
find('div.class input').click

#ページ全体で最初に該当するタグをクリック
first(input).click

#ページ全体で最後に該当するタグをクリック
last(input).click
```

#システムスペックのデバッグ
コンソールの出力からテストの結果を見ることができますが、Capybaraではヘッドレスブラウザ（UIを持たないブラウザ）を使ってRailsがブラウザに返したHTMLを見ることができます。

```rb
scenario "guest adds a project" do
  visit projects_path
  save_and_open_page  #この時点で返されるHTMLが保存される
  click_link "New Project"
end
```

save_and_open_pageでHTMLが保存されるので、それをブラウザ上で開くことで確認できます。

この機能は便利ですが、毎回手作業でファイルを開くのは面倒なので、Launchy gemをインストールして自動的に開くようにします。

```:Gemfile
group :test do
  gem 'launchy'
end
```

```
$ bundle install
```

#JavaScriptを使った操作のテスト

##準備
Railsでデフォルトでインストールされているselenium-webdriver gemによってテストで使うJavaScriptドライバを指定しますが、初期設定のFireFoxのままだと互換性問題があるようなので、Chromeを使うよう設定を変更します。

Capybaraの設定用ファイルを分けたいので、rails_helper.rbのファイル読み込み設定から始めていきます。

``` spec/rails_helper.rb
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
```

```spec/support/capybara.rb
Capybara.javascript_driver = :selenium_chrome_headless
```

CI環境の場合、テスト実行中にブラウザのウィンドウが新しく開くのは望ましくないため、そういった用件に泰雄するためにCapybaraでは**ヘッドレスドライバ**が使えるようになっています。ここでは、Chromeのヘッドレスモードを使ってテストを実行するように設定しています。

最後にWebdriver gemをインストールします。

```:Gemfile
group :test do
  gem 'webdrivers'
end
```

##テスト
js: trueオプションを渡すことで、JavaScriptを使った操作をテストすることができます。

```rb
scenario "test name", js: true do
  #テスト内容
end
```

##JavaScript待ち時間の設定
Capybaraではデフォルト設定の場合、処理の結果取得の待ち時間が2秒になっていますが、処理が間に合わずにテストが失敗することがあります。
これを回避するために待ち時間を任意で設定することもできます。

```spec/support/capybara.rb
Capybara.default_wait_time = 15 #15秒に設定
```

テストスイート全体でこの設定が適用されますが、テスト実行が必要以上に遅くなる可能性もあるため、必要に応じてusing_wait_timeを都度使うことも視野に入れると良いでしょう。

```rb
scenario "test" do
  using_wait_time(15) do  #このブロック内のみ待ち時間15秒になる
    #テスト内容
  end
end
```