【RSpec】Railsチュートリアル第6版　第13章

#はじめに
Railsチュートリアル第6版のテストをRSpecで書き直していく。

###目次
- [第3章](https://qiita.com/supyolo888/items/35081ead2cfafb40a995)
- [第4章](https://qiita.com/supyolo888/items/939b670b62eb18467f6d)
- [第5章](https://qiita.com/supyolo888/items/42234ac2ed3919b14586)
- [第6章](https://qiita.com/supyolo888/items/bd10e64c77fe391cf203)
- [第7章](https://qiita.com/supyolo888/items/d2824ced26402b5f58d1)
- [第8章](https://qiita.com/supyolo888/items/508caaa40edf307093ed)
- [第9章](https://qiita.com/supyolo888/items/62e471b07a0c9a81575d)
- [第10章](https://qiita.com/supyolo888/items/a8695b20ec8082f85223)
- [第11章](https://qiita.com/supyolo888/items/5c81331e46f1d9d5ab3c)
- [第12章](https://qiita.com/supyolo888/items/fbb95671d39dfbf9df90)
- 第13章（本記事）
- [第14章](https://qiita.com/supyolo888/items/532390902bf3e0f5348e)

#Minitest

###Micropostテスト

```rb:test/models/micropost_test.rb
require 'test_helper'

class MicropostTest < ActiveSupport::TestCase

  def setup
    @user = users(:michael)
    @micropost = @user.microposts.build(content: "Lorem ipsum")
  end

  test "should be valid" do
    assert @micropost.valid?
  end

  test "user id should be present" do
    @micropost.user_id = nil
    assert_not @micropost.valid?
  end

  test "content should be present" do
    @micropost.content = "   "
    assert_not @micropost.valid?
  end

  test "content should be at most 140 characters" do
    @micropost.content = "a" * 141
    assert_not @micropost.valid?
  end

  test "order should be most recent first" do
    assert_equal microposts(:most_recent), Micropost.first
  end

  test "associated microposts should be destroyed" do
    @user.save
    @user.microposts.create!(content: "Lorem ipsum")
    assert_difference 'Micropost.count', -1 do
      @user.destroy
    end
  end

  
end
```

###マイクロポスト用のfixture

```test/fixtures/microposts.yml
orange:
  content: "I just ate an orange!"
  created_at: <%= 10.minutes.ago %>
  user: michael

tau_manifesto:
  content: "Check out the @tauday site by @mhartl: https://tauday.com"
  created_at: <%= 3.years.ago %>
  user: michael

cat_video:
  content: "Sad cats are sad: https://youtu.be/PKffm2uI4dk"
  created_at: <%= 2.hours.ago %>
  user: michael

most_recent:
  content: "Writing a short test"
  created_at: <%= Time.zone.now %>
  user: michael

<% 30.times do |n| %>
micropost_<%= n %>:
  content: <%= Faker::Lorem.sentence(word_count: 5) %>
  created_at: <%= 42.days.ago %>
  user: michael
<% end %>

ants:
  content: "Oh, is that what you want? Because that's how you get ants!"
  created_at: <%= 2.years.ago %>
  user: archer

zone:
  content: "Danger zone!"
  created_at: <%= 3.days.ago %>
  user: archer

tone:
  content: "I'm sorry. Your words made sense, but your sarcastic tone did not."
  created_at: <%= 10.minutes.ago %>
  user: lana

van:
  content: "Dude, this van's, like, rolling probable cause."
  created_at: <%= 4.hours.ago %>
  user: lana
```

###Userプロフィール画面に対するテスト

```rb:test/integration/users_profile_test.rb
require 'test_helper'

class UsersProfileTest < ActionDispatch::IntegrationTest
  include ApplicationHelper

  def setup
    @user = users(:michael)
  end

  test "profile display" do
    get user_path(@user)
    assert_template 'users/show'
    assert_select 'title', full_title(@user.name)
    assert_select 'h1', text: @user.name
    assert_select 'h1>img.gravatar'
    assert_match @user.microposts.count.to_s, response.body
    assert_select 'div.pagination'
    @user.microposts.paginate(page: 1).each do |micropost|
      assert_match micropost.content, response.body
    end
  end
end
```

###Micropostsコントローラの認可テスト

```rb:test/controllers/microposts_controller_test.rb
require 'test_helper'

class MicropostsControllerTest < ActionDispatch::IntegrationTest

  def setup
    @micropost = microposts(:orange)
  end

  test "should redirect create when not logged in" do
    assert_no_difference 'Micropost.count' do
      post microposts_path, params: { micropost: { content: "Lorem ipsum" } }
    end
    assert_redirected_to login_url
  end

  test "should redirect destroy when not logged in" do
    assert_no_difference 'Micropost.count' do
      delete micropost_path(@micropost)
    end
    assert_redirected_to login_url
  end

  test "should redirect destroy for wrong micropost" do
    log_in_as(users(:michael))
    micropost = microposts(:ants)
    assert_no_difference 'Micropost.count' do
      delete micropost_path(micropost)
    end
    assert_redirected_to root_url
  end
end
```

###マイクロポストのUIに対する統合テスト

```rb:test/integration/microposts_interface_test.rb
require 'test_helper'

class MicropostsInterfaceTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:michael)
  end

  test "micropost interface" do
    log_in_as(@user)
    get root_path
    assert_select 'div.pagination'
    # 無効な送信
    assert_no_difference 'Micropost.count' do
      post microposts_path, params: { micropost: { content: "" } }
    end
    assert_select 'div#error_explanation'
    assert_select 'a[href=?]', '/?page=2'  # 正しいページネーションリンク
    # 有効な送信
    content = "This micropost really ties the room together"
    assert_difference 'Micropost.count', 1 do
      post microposts_path, params: { micropost: { content: content } }
    end
    assert_redirected_to root_url
    follow_redirect!
    assert_match content, response.body
    # 投稿を削除する
    assert_select 'a', text: 'delete'
    first_micropost = @user.microposts.paginate(page: 1).first
    assert_difference 'Micropost.count', -1 do
      delete micropost_path(first_micropost)
    end
    # 違うユーザーのプロフィールにアクセス（削除リンクがないことを確認）
    get user_path(users(:archer))
    assert_select 'a', text: 'delete', count: 0
  end
end
```

#RSpec 

###Micropostテスト

```rb:spec/models/micropost_spec.rb
require 'rails_helper'

RSpec.describe Micropost, type: :model do

  let!(:user) { FactoryBot.create(:user) }
  let!(:tau_manifesto) {FactoryBot.create(:micropost, :tau_manifesto)}
  let!(:cat_video) {FactoryBot.create(:micropost,:cat_video)}
  let!(:most_recent) {FactoryBot.create(:micropost,:most_recent)}
  let!(:micropost) {user.microposts.build(content: "Lorem ipsum")}

  it "should be valid" do
    expect(micropost).to be_valid
  end

  it "user id should be present" do
    micropost.user_id = nil
    expect(micropost).to_not be_valid
  end

  it "content should be present" do
    micropost.content = "   "
    expect(micropost).to_not be_valid
  end

  it "content should be at most 140 characters" do
    micropost.content = "a" * 141
    expect(micropost).to_not be_valid
  end

  it "order should be most recent first" do
    expect(most_recent).to eq Micropost.first
  end

  it "associated microposts should be destroyed" do
    user.microposts.create!(content: "Lorem ipsum")
    expect {
      user.destroy
    }.to change(Micropost, :count).by(-1)
  end

end
```

`let!`においてcreated_atがバラバラのmicropostを事前に用意しておく。

###マイクロポスト用のfactory

```rb:spec/factories/microposts.rb
FactoryBot.define do

  factory :micropost do

    content {"I just ate an orange!"}
    created_at {10.minutes.ago}
    association :user

    trait :tau_manifesto do
      content {"Check out the @tauday site by @mhartl: https://tauday.com"}
      created_at {3.years.ago}
    end

    trait :cat_video do
      content {"Sad cats are sad: https://youtu.be/PKffm2uI4dk"}
      created_at {2.hours.ago}
    end

    trait :most_recent do
      content {"Writing a short test"}
      created_at{Time.zone.now}
    end

  end
  
end
```


###Userプロフィール画面に対するテスト

```rb:spec/requests/users_profiles_spec.rb
require 'rails_helper'
include ApplicationHelper

RSpec.describe "UsersProfiles", type: :request do

  let(:user) {FactoryBot.create(:user)}

  before do
    40.times do
      user.microposts.create(content: Faker::Lorem.sentence(word_count: 5))
    end
  end

  it "profile display" do
    get user_path(user)
    expect(response).to have_http_status(200)
    assert_select "title", full_title(user.name)
    assert_select "h1", text: user.name
    assert_select "h1>img.gravatar"
    assert_match user.microposts.count.to_s, response.body
    assert_select "div.pagination"
    user.microposts.paginate(page: 1).each do |micropost|
      assert_match micropost.content, response.body
    end
  end
  
end
```

*full_title*メソッドを使用する為に、*include ApplicationHelper*を記述しておく。
before do において、異なるcontentを持つmicropostを40個用意しておく（ページネーションを使いたいので30個以上にしておく）。

###Micropostsコントローラの認可テスト

```rb:spec/requests/microposts_spec.rb
require 'rails_helper'

RSpec.describe "Microposts", type: :request do

  let!(:micropost) { FactoryBot.create(:micropost) }
  let!(:user1) { FactoryBot.create(:user) }
  
  it "should redirect create when not logged in" do
    expect{
      post microposts_path, params: { micropost: { content: "Lorem ipsum" } }
    }.to_not change(Micropost, :count)
    expect(response).to redirect_to login_path
  end

  it "should redirect destroy when not logged in" do
    expect {
      delete micropost_path(micropost)
    }.to_not change(Micropost, :count)
    expect(response).to redirect_to login_path
  end

  it "should redirect destroy for wrong micropost" do
    log_in_as(user1)
    expect {
      delete micropost_path(micropost)
    }.to_not change(Micropost, :count)
    expect(response).to redirect_to root_path
  end

end
```

*it "should redirect destroy for wrong micropost" do*において、micropost作成者以外がログインしている状態で削除しようとすると失敗することをテストするため、`let!`でuser1を作成しておく。

###マイクロポストのUIに対する統合テスト

```rb:spec/requests/microposts_interface_spec.rb
require 'rails_helper'

RSpec.describe "MicropostsInterface", type: :request do

  let!(:user) { FactoryBot.create(:user) }
  let!(:user1) { FactoryBot.create(:user) }

  before do
    40.times do
      user.microposts.create(content: Faker::Lorem.sentence(word_count: 5))
    end
  end

  it "micropost interface" do
    log_in_as(user)
    get root_path
    assert_select "div.pagination"
    expect{
      post microposts_path, params: { micropost: { content: "" } }
    }.to_not change(Micropost, :count)
    assert_select "div#error_explanation"
    assert_select "a[href=?]", "/?page=2"
    content = "This micropost really ties the room together"
    expect{
      post microposts_path, params: { micropost: { content: content } }
    }.to change(Micropost, :count).by(1)
    expect(response).to redirect_to root_path
    follow_redirect!
    #assert_match content, response.body
    assert_select "a", text: "delete"
    first_micropost = user.microposts.paginate(page: 1).first
    expect{
      delete micropost_path(first_micropost)
    }.to change(Micropost, :count).by(-1)
    get user_path(user1)
    assert_select "a", text: "delete", count:0
  end

end
```

#注意点
factoryにおいて

```rb:spec/factories/microposts.rb
factory :micropost do

    content {"I just ate an orange!"}
    created_at {10.minutes.ago}
    association :user
    .
    .
    .
```

というように、associationでmicropostに関連づけられるuserを作成しているが、テスト内で`let!(:user) { FactoryBot.create(:user) }`とした場合、user factoryはシーケンスにより異なるメールアドレスを持つ別のユーザーが作成されることになる。例えばテスト内でuserとしてログインした状態でmicropostを削除しようとするとmicropostを作成したユーザーでないために失敗することになる。
よってページネーションのテストを行う際、

```rb
before do
    40.times {FactoryBot.create(:micropost)}
end
```

ではなく

```rb
before do
    40.times do
      user.microposts.create(content: Faker::Lorem.sentence(word_count: 5))
    end
end
```

と記述する必要がある。