0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-11-11

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

###目次

#Minitest

###Micropostテスト

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プロフィール画面に対するテスト

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コントローラの認可テスト

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に対する統合テスト

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テスト

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

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プロフィール画面に対するテスト

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コントローラの認可テスト

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に対する統合テスト

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において

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を作成したユーザーでないために失敗することになる。
よってページネーションのテストを行う際、

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

ではなく

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

と記述する必要がある。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?