RailsチュートリアルテストをRSpecで実施 【フィーチャスペック編 users_signup_spec 2/7】

<!--
# RailsチュートリアルテストをRSpecでやってみる 【フィーチャスペック編 users_signup_spec 2/7】
-->
<!-- ## フィーチャスペック編 users_signup_spec 2/7 -->


### テスト対象

該当するコントローラ  | 該当するアクション
:--|:--
UsersController  |  new, create

### 環境
[Userモデル 単体テスト編 1/3](https://qiita.com/shu130/items/57233462258d42566f18) に記載

#### フォルダ、使用ファイル

種類  |  ファイル名
:--|:--
スペック  |  spec/features/users_signup_spec.rb
サポートモジュール  |  spec/support/support_module.rb
shared_context  |  spec/support/shared_context.rb
shared_examples  |  spec/support/shared_examples.rb
ファクトリ（ユーザ）  |  spec/support/factories/users.rb


### アウトラインの作成

```ruby:users_signup_spec.rb
spec/features/users_signup_spec.rb

RSpec.feature "UsersSignup", type: :feature do

  # サインアップページ
  describe "signup"
    # signupフォームが正しいこと
    scenario "sign up form have right css"

    # 情報が valid
    context "valid info"
      # 成功 (increment: 1)
      scenario "success create user"
      # successメッセージがあること
      scenario "success messages"
      # profile-page へリダイレクトすること
      scenario "redirect to profile-page"

    # 情報が invalid
    context "invalid info"
      # 失敗 (increment: 0)
      scenario "fail create user"
      # errorメッセージがあること
      scenario "error messages"
      # サインアップページのまま
      scenario "render signup-page"
end
```

### スペック作成

##### 以下の部分を、shared_examples（別ファイル）に作成

- `scenario "sign up form have right css"`
- `scenario "success create user"`
- `scenario "fail create user"`

```ruby:users_signup_spec.rb
spec/features/users_signup_spec.rb
require 'rails_helper'

RSpec.feature "UsersSignup", type: :feature do

  include SupportModule
  # subject { page }

  describe "signup" do
    before { visit "/signup" }
    it_behaves_like "signup-form have right css"
    # normal
    # valid な情報の場合
    context "valid info" do
      # 成功 (increment: 1)
      it_behaves_like "success create user"
    end
    # abnomal
    # invalid な情報の場合
    context "invalid info" do
      # 失敗 (increment: 0)
      it_behaves_like "fail create user"
    end
  end
end
```

#### shared_examples の作成

- `it_behaves_like "signup-form have right css"`
- `it_behaves_like "success create user"`
- `it_behaves_like "fail create user"`

  ```ruby:shared_examples.rb
  spec/support/shared_examples.rb

                      # フォーム

    # サインアップ
    # users#new
    shared_examples_for "signup-form have right css" do
      it { expect(page).to have_css('label', text: 'Name') }
      it { expect(page).to have_css('label', text: 'Email') }
      it { expect(page).to have_css('label', text: 'Password') }
      it { expect(page).to have_css('label', text: 'Confirmation') }
      it { expect(page).to have_css('input#user_name') }
      it { expect(page).to have_css('input#user_email') }
      it { expect(page).to have_css('input#user_password') }
      it { expect(page).to have_css('input#user_password_confirmation') }
      it { expect(page).to have_button('Create my account') }
    end

                    # ユーザの作成

    # ユーザの作成（一般ユーザ）
    # users#create
    # 成功
    shared_examples_for "success create user" do
      scenario "user increment 1" do
        expect {
          visit signup_path
          fill_in_signup_form(:user) # => SupportModule を使用
          click_button "Create my account"
        }.to change(User, :count).by(1)
        # 成功メッセージ
        expect(page).to have_css("div.alert.alert-success", text: "Welcome to the Sample App!")
        # profile-page へリダイレクトしていること
        expect(page).to have_current_path(user_path(User.last))
        expect(current_path).to eq user_path(User.last)
      end
    end
    # 失敗
    shared_examples_for "fail create user" do
      scenario "user increment 0" do
        expect {
          visit signup_path
          fill_in_signup_form(:user, invalid: true) # => SupportModule を使用
          click_button "Create my account"
        }.to change(User, :count).by(0)
        # 失敗メッセージ
        expect(page).to have_css('div.alert.alert-danger', text: "errors")
        # サインアップページのまま
        expect(page).to have_title("Sign up")
        expect(page).to have_css("h1", text: "Sign up")
      end
    end
  ```

#### SupportModule の作成

##### 以下の部分を、SupportModule（別ファイル）に作成

- `fill_in_signup_form(:user)`
- `fill_in_signup_form(:user, invalid: true)`

  ```ruby:support_module.rb
  spec/support/support_module.rb

  module SupportModule

    def fill_in_signup_form(user, option = { invalid: false })
      if option[:invalid]
        fill_in "Name",         with: ""
        fill_in "Email",        with: ""
        fill_in "Password",     with: ""
        fill_in "Confirmation", with: ""
      else
        # 属性値をハッシュとして作成
        params = attributes_for(user) # => ファクトリ使用
        fill_in "Name",         with: params[:name]
        fill_in "Email",        with: params[:email]
        fill_in "Password",     with: params[:password]
        fill_in "Confirmation", with: params[:password]
      end
    end
  end
  ```

#### ファクトリの作成（ユーザ）

- `params = attributes_for(user)` の部分

  ```ruby:users.rb
  # spec/factories/users.rb
  FactoryBot.define do
    # 自分
    # factory [ファクトリ名], class: [クラス名]
    factory :user, class: User do
      name     "Example user"
      email    "user@example.com"
      password              "foobar"
      password_confirmation "foobar"
      admin false
    end
  end
  ```

##### 補足 `FactoryBot.attributes_for(:user)`

- 属性値をハッシュ化する

  ```test
  $ rails console test --sandbox
  [1] pry(main)>
  [2] pry(main)> u = FactoryBot.attributes_for(:user)
  => {:name=>"Example user",
   :email=>"user@example.com",
   :password=>"foobar",
   :password_confirmation=>"foobar",
   :admin=>false}
  ```


### 実行結果

```test
$ bin/rspec spec/features/users_signup_spec.rb

UsersSignup
  signup
    behaves like signup-form have right css
      should have visible css "label" with text "Name"
      should have visible css "label" with text "Email"
      should have visible css "label" with text "Password"
      should have visible css "label" with text "Confirmation"
      should have visible css "input#user_name"
      should have visible css "input#user_email"
      should have visible css "input#user_password"
      should have visible css "input#user_password_confirmation"
      should have visible button "Create my account"
    valid info
      behaves like success create user
        user increment 1
    invalid info
      behaves like fail create user
        user increment 0

Finished in 6.22 seconds (files took 2.21 seconds to load)
11 examples, 0 failures

```


<!-- 1 of ? -->
--------------------------------

### 参考

- [Railsチュートリアル Rails4.0(第2版)](https://railstutorial.jp/chapters/modeling-users?version=4.0#top)

- [(書籍)leanpub - Everyday Rails - RSpecによるRailsテスト入門](https://leanpub.com/everydayrailsrspec-jp/)

--------------------------------


### 続く
[フィーチャスペック編 users_login_spec 3/7](https://qiita.com/shu130/items/24a35309ca7320cb5e68)