Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

ツイッター風Railsアプリをテストする(統合テスト編)

この記事の基本的な方針

今回は別記事、ツイッター風Railsアプリ最短復習(忙しい人の流し読みで開発シリーズ)で作ったアプリの統合テストをします。

手を動かしながら読みたいようでしたら、以下でこのツイッター風Railsアプリを手に入れてください。

Terminal
$ git clone https://github.com/annaPanda8170/cheaptweet.git
$ bundle install
$ bundle exec rake db:create
$ bundle exec rake db:migrate

基本解説はしません。手順のみ示します。

想定する読み手

既に一度Railsアプリをチュートリアルやスクール等で作ったことがある方を想定しております。
Mac使用で、パソコンの環境構築は完了していることが前提です。

具体的な手順

完成品GitHub

①準備

Gemfile
#省略
group :development, :test do
#省略
  gem 'rspec-rails'
  gem 'factory_bot_rails'
end

group :development do
#省略
  gem 'spring-commands-rspec'
end
#省略

※spring-commands-rspecは起動時間を速くするためのものでなくても問題はありません。

Terminal
$ bundle install
$ rails g rspec:install
$ rails g rspec:feature cheapTweet
$ rails g factory_bot:model user
$ rails g factory_bot:model tweet
$ bundle exec spring binstub rspec

control + c
$ rails s
.rspec
--format documentation

※これでRspecの出力が読みやすくなるそうです。

spec/rails_helper.rb
#省略
require 'capybara/rspec'
include Warden::Test::Helpers

ここで空っぽのまま一度起動してみます。

Terminal
$ bundle exec rspec
Output
CheapTweets
  add some scenarios (or delete) /Users/handaryouhei/Desktop/cheaptweet/spec/features/cheap_tweet_spec.rb (PENDING: Not yet implemented)

Pending: (Failures listed here are expected and do not affect your suite's status)

  1) CheapTweets add some scenarios (or delete) /Users/handaryouhei/Desktop/cheaptweet/spec/features/cheap_tweet_spec.rb
     # Not yet implemented
     # ./spec/features/cheap_tweet_spec.rb:4


Finished in 0.0014 seconds (files took 2.21 seconds to load)
1 example, 0 failures, 1 pending
spec/factories/tweets.rb
FactoryBot.define do
  factory :tweet do
    text {"hello"}
    association :user
  end
end
spec/factories/users.rb
FactoryBot.define do
  factory :user do
    sequence(:nickname) { |n| "annaPanda#{n}" }
    sequence(:email) { |n| "a#{n}@a" }
    password {"111111"}
    password_confirmation {"111111"}
  end
end

②テスト構築

spec/features/cheap_tweet_spec.rb
require 'rails_helper'

RSpec.feature "CheapTweets", type: :feature do
  scenario "新規登録するとログアウトが表示されているTOP画面に遷移する" do
    visit root_path
    click_link "会員登録"
    fill_in "user_nickname", with: "annaPanda"
    fill_in "user_email", with: "a@a"
    fill_in "user_password", with: "111111"
    fill_in "user_password_confirmation", with: "111111"
    click_button "Sign up"
    expect(page).to have_content 'ログアウト'
  end
  scenario "ログインするとログアウトが表示されているTOP画面に遷移する" do
    user = FactoryBot.create(:user)
    visit root_path
    click_link "ログイン"
    fill_in "user_email", with: "a1@a"
    fill_in "user_password", with: "111111"
    click_button "Log in"
    expect(page).to have_content 'ログアウト'
  end
  scenario "非ログイン状態でTOP画面に遷移するとログアウトが表示されていない" do
    visit root_path
    expect(page).not_to have_content 'ログアウト'
  end
  scenario "投稿したらデータベースにtweetが一つ増える" do
    user = FactoryBot.create(:user)
    login_as(user, scope: :user)
    visit root_path
    expect{
      click_link "投稿"
      fill_in "tweet_text", with: "こんにちは"
      click_button "投稿"
    }.to  change( Tweet.all, :count ).by(1)
  end
  scenario "自分で投稿したtweetをshowして編集するとindexに反映される" do
    user = FactoryBot.create(:user)
    login_as(user, scope: :user)
    visit root_path
    click_link "投稿"
    fill_in "tweet_text", with: "こんにちは"
    click_button "投稿"
    click_link "こんにちは"
    click_link '編集'
    fill_in "tweet_text", with: "こんにちは!"
    click_button '編集'
    visit root_path
    expect(page).to have_content 'こんにちは!'
  end
  scenario "自分以外が投稿したtweetをshowすれば編集ボタンが表示されない" do
    tweet = FactoryBot.create(:tweet)
    visit root_path
    click_link "a"
    expect(page).not_to have_content '編集'
  end
  scenario "自分で投稿したtweetをshowして削除ボタンを押せばデータベースのtweetが一つ減る" do
    user = FactoryBot.create(:user)
    login_as(user, scope: :user)
    visit root_path
    click_link "投稿"
    fill_in "tweet_text", with: "こんばんは"
    click_button "投稿"
    click_link "こんばんは"
    expect{
      click_link "削除"
    }.to  change( Tweet.all, :count ).by(-1)
  end
  scenario "自分以外が投稿したtweetをshowすれば削除ボタンが表示されない" do
    tweet = FactoryBot.create(:tweet)
    visit root_path
    click_link "hello"
    expect(page).not_to have_content '削除'
  end
  scenario "ヘッダーの自分の名前をクリックすると自分の投稿一覧が表示される" do
    tweet = FactoryBot.create(:tweet)
    user = FactoryBot.create(:user)
    login_as(user, scope: :user)
    visit root_path
    click_link "投稿"
    fill_in "tweet_text", with: "おはよう"
    click_button "投稿"
    click_link "投稿"
    fill_in "tweet_text", with: "さようなら"
    click_button "投稿"
    within 'header' do
      click_link user.nickname
    end
    expect(page).to have_content 'おはよう'
    expect(page).to have_content 'さようなら'
    expect(page).not_to have_content 'hello'
  end
  scenario "TOP画面の投稿者をクリックするとそのユーザーの一覧が表示される" do
    tweet = FactoryBot.create(:tweet)
    user = FactoryBot.create(:user)
    login_as(user, scope: :user)
    visit root_path
    click_link "投稿"
    fill_in "tweet_text", with: "おはよう"
    click_button "投稿"
    click_link "投稿"
    fill_in "tweet_text", with: "さようなら"
    click_button "投稿"
    click_link tweet.user.nickname
    expect(page).not_to have_content 'おはよう'
    expect(page).not_to have_content 'さようなら'
    expect(page).to have_content 'hello'
  end
  scenario "コメントすると表示される" do
    tweet = FactoryBot.create(:tweet)
    user = FactoryBot.create(:user)
    login_as(user, scope: :user)
    visit root_path
    click_link "hello"
    fill_in "comment_text", with: "ハロー"
    click_button "コメント"
    expect(page).to have_content 'ハロー'
  end
end

click_linkclick_buttonで指定しているのはテキストです。
fill_inで指定しているのはidです。
have_contentで表示されているかを確認しています。

Terminal
$ bundle exec rspec
Output
CheapTweets
  新規登録するとログアウトが表示されているTOP画面に遷移する
  ログインするとログアウトが表示されているTOP画面に遷移する
  非ログイン状態でTOP画面に遷移するとログアウトが表示されていない
  投稿したらデータベースにtweetが一つ増える
  自分で投稿したtweetをshowして編集するとindexに反映される
  自分以外が投稿したtweetをshowすれば編集ボタンが表示されない
  自分で投稿したtweetをshowして削除ボタンを押せばデータベースのtweetが一つ減る
  自分以外が投稿したtweetをshowすれば削除ボタンが表示されない
  ヘッダーの自分の名前をクリックすると自分の投稿一覧が表示される
  TOP画面の投稿者をクリックするとそのユーザーの一覧が表示される
  コメントすると表示される

Finished in 0.87206 seconds (files took 2.18 seconds to load)
11 examples, 0 failures

まとめ

読むだけなら割と直感的にいけると思います。
この統合テストさえ済ませていれば、コントローラは不要なのでは?という考えもあるようです。
モデルのテストとこの統合テストを行うことがRailsアプリのテストのスタンダードになるかもしれませんね。

annaPanda8170
長いコードを"上から順に"説明されても理解できないですよね? "必要な順に"コードを増やしてゆくスタイルで記事を書いていくつもりです。 私の記事は半分パブリックなものだと思っているので、どんどん意見を吸収して育てていきたいです。
https://annapanda.xyz/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away