Railsチュートリアルの第４版をRSpecでテスト-1

[Ruby on Rails チュートリアル][link-1]の第4版を学習し、[Everyday Rails - RSpecによるRailsテスト入門][link-2]でRSpecを学習したので、[Ruby on Rails チュートリアル][link-1]をRSpecでテストしてみました。
今回は第3章～第6章までを書きます。

[link-1]:https://railstutorial.jp/
[link-2]:https://leanpub.com/everydayrailsrspec-jp
[link-3]:https://qiita.com/yano12/items/eba8693272c31d6af7f0
[link-4]:https://qiita.com/yano12/items/4ef93129a50249adcb8d
[link-5]:https://qiita.com/yano12/items/319615e6e5fdcd7d844a
[link-6]:https://qiita.com/yano12/items/99e3fd4213f61339bad4
[link-7]:https://railstutorial.jp/chapters/account_activation?version=5.1#sec-exercises_mailer_templates
[link-8]:https://qiita.com/yano12/items/f5167416f3c20e1896d4
[link-9]:https://railstutorial.jp/chapters/password_reset?version=5.1#cha-password_reset
[link-10]:https://qiita.com/IrieLuka/items/34b0edccecc036d24748

# Railsチュートリアル第3～5章のテスト
今回はHomeページのみテストしました。(Help、About、Contactページはテストしない。)
まず初めに

```
$ bin/rails g rspec:feature static_page
```

でspec/features/static_pages_spec.rbを作成し、Homeページが正しく表示されていること、タイトルが正しいことをテストします。

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

RSpec.feature "StaticPages", type: :feature do
  describe "Home page" do
    before do
      visit root_path   # 名前付きルートを使用
    end
    
    # HomeページにStaticPages#homeと表示されていること
    it "should have the content 'StaticPages#home'" do
      expect(page).to have_content "StaticPages#home"
    end
    
    # タイトルが正しく表示されていること
    it "should have the right title" do
      expect(page).to have_title full_title('')
    end
  end
end
```

さらに、spec/support/utilities.rbを作成しfull_titleメソッドを定義します。

```spec/support/utilities.rb
def full_title(page_title)
  base_title = "Ruby on Rails Tutorial Sample App"
  if page_title.empty?
    base_title
  else
    "#{base_title} | #{page_title}"
  end
end
```

実行するとテストがパスします。

```
StaticPages
  Home page
    should have the content 'StaticPages#home'
    should have the right title

Finished in 0.27791 seconds (files took 0.22495 seconds to load)
2 examples, 0 failures
```

`spec/support/utilities.rb`が自動で読み込まれない場合は、[RSpec3でspec/support内のファイルを読み込む](https://qiita.com/necojackarc/items/17bbeae646e331eb5649)を参考にしてください。

同じように新規登録ページもテストします。

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

RSpec.feature "UserPages", type: :feature do
  describe "signup page" do
    before do
      visit signup_path
    end
    
    # 新規登録ページに"Sign up"と表示されていること
    it "should have the content 'Sign up'" do
      expect(page).to have_content "Sign up"
    end
    
    # タイトルが正しく表示されていること
    it "should have the right title" do
      expect(page).to have_title full_title('Sign up')
    end
  end
end
```

# Railsチュートリアル第6章のテスト
続いてUserモデルのバリデーションをテストしていきます。
ファクトリを使ってテストデータのセットアップを行うためにGemfileにFactory Botをインストールします。
また、テストを簡略化するためにShoulda Matchersを使用します。
(gemのバージョンは[Everyday Rails - RSpecによるRailsテスト入門][link-2]を参照しました。)

```Gemfile.
group :development, :test do
  gem 'rspec-rails', '~> 3.6.0'
  gem 'factory_bot_rails', '~> 4.10.0'
  ・・・
end

group :test do
　# 他のgemは省略
  gem 'shoulda-matchers',
    git: 'https://github.com/thoughtbot/shoulda-matchers.git',
    branch: 'rails-5'
end
```

さらに、Shoulda Matchersを使用するためにspec/rails_helper.rbの一番最後に以下のコードを追加します。

```spec/rails_helper.rb
Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec
    with.library :rails 
  end 
end
```

コードを追加したら

```
$ bin/rails g factory_bot:model user
```


でspec/factories/users.rbを作成し、以下のようにテストデータを作成します。

```spec/factories/users.rb
FactoryBot.define do
  factory :user do
    name "Example"
    sequence(:email) { |n| "tester#{n}@example.com" }
    password "password"
    password_confirmation "password"
  end
end
```

続いてspec/models/user_spec.rbを作成し、name、email、passwordのバリデーションをテストします。

```spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  let(:user) { FactoryBot.create(:user) }
  
  describe User do 
    # 有効なファクトリを持つこと
    it "has a valid factory" do 
      expect(user).to be_valid
    end
  end
  
  # Shoulda Matchers を使用
  it { is_expected.to validate_presence_of :name }
  it { is_expected.to validate_length_of(:name).is_at_most(50) }
  it { is_expected.to validate_presence_of :email }
  #it { is_expected.to validate_uniqueness_of(:email).case_insensitive } ・・・(*)
  it { is_expected.to validate_length_of(:email).is_at_most(255) }
  it { is_expected.to validate_presence_of :password }
  it { is_expected.to validate_length_of(:password).is_at_least(6) }
  
  # 重複したメールアドレスなら無効な状態であること ・・・(*)
  it "is invalid with a duplicate email adress" do 
    FactoryBot.create(:user, email: "aaron@example.com")
    user = FactoryBot.build(:user, email: "Aaron@example.com")
    user.valid?
    expect(user.errors[:email]).to include("has already been taken")
  end
  
  # メールアドレスの有効性
  describe "email validation should reject invalid addresses" do
    # 無効なメールアドレスの場合
    it "should be invalid" do
      invalid_addresses = %w[user@example,com user_at_foo.org user.name@example. 
                            foo@bar_baz.com foo@bar+baz.com]
      invalid_addresses.each do |invalid_address|
        user.email = invalid_address
        expect(user).to_not be_valid
      end
    end
  
    # 有効なメールアドレスの場合
    it "should be valid" do
      valid_addresses = %w[user@foo.COM A_US-ER@f.b.org frst.lst@foo.jp a+b@baz.cn]
      valid_addresses.each do |valid_address|
        user.email = valid_address
        expect(user).to be_valid
      end
    end
  end
  
  # パスワード確認が一致すること
  describe "when password doesn't match confirmation" do
    # 一致する場合
    it "is valid when password confirmation matches password" do
      user = FactoryBot.build(:user, password: "password", password_confirmation: "password")
      expect(user).to be_valid
    end
    
    # 一致しない場合
    it "is invalid when password confirmation doesn't match password" do
      user = FactoryBot.build(:user, password: "password", password_confirmation: "different")
      user.valid?
      expect(user.errors[:password_confirmation]).to include("doesn't match Password")
    end
  end
end
```

メールアドレスの重複のテスト(*)はShoulda Matchersを使うと上手くいかなかったので、通常のテスト(簡略化していない)を実行しました。
テストを実行してパスするのを確認します。

```
Finished in 0.09507 seconds (files took 0.15479 seconds to load)
12 examples, 0 failures
```

今回は以上です。
第7章以降のテストは完成しだい書いていきます。

#追記
第7章以降のテストを書きました。
 - [Railsチュートリアルの第4版をRSpecでテスト-2][link-4]（第7章～第8章）
 - [Railsチュートリアルの第4版をRSpecでテスト-3][link-5]（第9章）
 - [Railsチュートリアルの第4版をRSpecでテスト-4][link-6]（第10章）
 - [Railsチュートリアルの第4版をRSpecでテスト-5][link-8]（第11章）
 - [Railsチュートリアルの第4版をRSpecでテスト-6][link-10]（第12章）