記事概要
Ruby on Railsの結合テストについて、まとめる
前提
- Ruby on Railsでアプリケーションを作成している
- アプリにCapybaraをインストールしている
System Spec
結合テストコードを記述するための仕組みのこと
Capybara
というGemを使用し、デフォルトでRailsに搭載されている
手順(テストファイル作成)
- テストファイルを生成するため、下記コマンドを実行する
# アプリのディレクトリに移動 % cd ~/[アプリ名] % rails g rspec:system [モデル名の複数形]
-
spec/system/[モデル名の複数形]_spec.rb
が作成されたことを確認する
手順(テストケースの記述)
-
[モデル名の複数形]_spec.rbにテストケースを記述する
- 機能別・条件別・テスト詳細別にグループ分けを行う
※ユーザー目線で考え、「ブラウザでどのような操作をすると、どうなるのか」をテストケースに反映する - 保存するデータ(インスタンス)を作成する
- テストコードを記述する
※各テストコードが想定通りに実行できるかを確認するため、rails c
コマンドでコンソールを起動して確認を行う
- 機能別・条件別・テスト詳細別にグループ分けを行う
手順(テストファイルの実行)
- テストファイルを実行するため、下記コマンドを実行する
# アプリのディレクトリに移動 % cd ~/[アプリ名] % bundle exec rspec spec/system/[モデル名の複数形]_spec.rb
- ターミナル.appで正常完了していることを確認する
結果が緑色で表示されれば実行成功
※ターミナルにDEPRECATION WARNING
と表示されることがあるが、無視して問題ない
参考コード
ユーザー新規登録
require 'rails_helper'
RSpec.describe 'ユーザー新規登録', type: :system do
before do
@user = FactoryBot.build(:user)
end
context 'ユーザー新規登録ができるとき' do
it '正しい情報を入力すればユーザー新規登録ができてトップページに移動する' do
# トップページに移動する
visit root_path
# トップページにサインアップページへ遷移するボタンがあることを確認する
expect(page).to have_content('新規登録')
# 新規登録ページへ移動する
visit new_user_registration_path
# ユーザー情報を入力する
fill_in 'Nickname', with: @user.nickname
fill_in 'Email', with: @user.email
fill_in 'Password', with: @user.password
fill_in 'Password confirmation', with: @user.password_confirmation
# サインアップボタンを押すとユーザーモデルのカウントが1上がることを確認する
expect{
find('input[name="commit"]').click
sleep 1
}.to change { User.count }.by(1)
# トップページへ遷移することを確認する
expect(page).to have_current_path(root_path)
# カーソルを合わせるとログアウトボタンが表示されることを確認する
expect(
find('.user_nav').find('span').hover
).to have_content('ログアウト')
# サインアップページへ遷移するボタンやログインページへ遷移するボタンが表示されていないことを確認する
expect(page).to have_no_content('新規登録')
expect(page).to have_no_content('ログイン')
end
end
context 'ユーザー新規登録ができないとき' do
it '誤った情報ではユーザー新規登録ができずに新規登録ページへ戻ってくる' do
# トップページに移動する
visit root_path
# トップページにサインアップページへ遷移するボタンがあることを確認する
expect(page).to have_content('新規登録')
# 新規登録ページへ移動する
visit new_user_registration_path
# ユーザー情報を入力する
fill_in 'Nickname', with: ''
fill_in 'Email', with: ''
fill_in 'Password', with: ''
fill_in 'Password confirmation', with: ''
# サインアップボタンを押してもユーザーモデルのカウントは上がらないことを確認する
expect{
find('input[name="commit"]').click
}.to change { User.count }.by(0)
# 新規登録ページへ戻されることを確認する
expect(page).to have_current_path(new_user_registration_path)
end
end
end
RSpec.describe 'ログイン', type: :system do
before do
@user = FactoryBot.create(:user)
end
context 'ログインができるとき' do
it '保存されているユーザーの情報と合致すればログインができる' do
# トップページに移動する
visit root_path
# トップページにログインページへ遷移するボタンがあることを確認する
expect(page).to have_content('ログイン')
# ログインページへ遷移する
visit new_user_session_path
# 正しいユーザー情報を入力する
fill_in 'Email', with: @user.email
fill_in 'Password', with: @user.password
# ログインボタンを押す
find('input[name="commit"]').click
# トップページへ遷移することを確認する
expect(page).to have_current_path(root_path)
# カーソルを合わせるとログアウトボタンが表示されることを確認する
expect(
find('.user_nav').find('span').hover
).to have_content('ログアウト')
# サインアップページへ遷移するボタンやログインページへ遷移するボタンが表示されていないことを確認する
expect(page).to have_no_content('新規登録')
expect(page).to have_no_content('ログイン')
end
end
context 'ログインができないとき' do
it '保存されているユーザーの情報と合致しないとログインができない' do
# トップページに移動する
visit root_path
# トップページにログインページへ遷移するボタンがあることを確認する
expect(page).to have_content('ログイン')
# ログインページへ遷移する
visit new_user_session_path
# ユーザー情報を入力する
fill_in 'Email', with: ''
fill_in 'Password', with: ''
# ログインボタンを押す
find('input[name="commit"]').click
# ログインページへ戻されることを確認する
expect(page).to have_current_path(new_user_session_path)
end
end
end
ツイート
require 'rails_helper'
RSpec.describe 'ツイート投稿', type: :system do
before do
@user = FactoryBot.create(:user)
@tweet_text = Faker::Lorem.sentence
@tweet_image = Faker::Lorem.sentence
end
context 'ツイート投稿ができるとき'do
it 'ログインしたユーザーは新規投稿できる' do
# ログインする
sign_in(@user)
# 新規投稿ページへのボタンがあることを確認する
expect(page).to have_content('投稿する')
# 投稿ページに移動する
visit new_tweet_path
# フォームに情報を入力する
fill_in 'tweet_image', with: @tweet_image
fill_in 'tweet_text', with: @tweet_text
# 送信するとTweetモデルのカウントが1上がることを確認する
expect{
find('input[name="commit"]').click
sleep 1
}.to change { Tweet.count }.by(1)
# トップページには先ほど投稿した内容のツイートが存在することを確認する(画像)
expect(page).to have_selector ".content_post[style='background-image: url(#{@tweet_image});']"
# トップページには先ほど投稿した内容のツイートが存在することを確認する(テキスト)
expect(page).to have_content(@tweet_text)
end
end
context 'ツイート投稿ができないとき'do
it 'ログインしていないと新規投稿ページに遷移できない' do
# トップページに遷移する
visit root_path
# 新規投稿ページへのボタンがないことを確認する
expect(page).to have_no_content('投稿する')
end
end
end
RSpec.describe 'ツイート編集', type: :system do
before do
@tweet1 = FactoryBot.create(:tweet)
@tweet2 = FactoryBot.create(:tweet)
end
context 'ツイート編集ができるとき' do
it 'ログインしたユーザーは自分が投稿したツイートの編集ができる' do
# ツイート1を投稿したユーザーでログインする
sign_in(@tweet1.user)
# ツイート1に「編集」へのリンクがあることを確認する
expect(
all('.more')[1].hover
).to have_link '編集', href: edit_tweet_path(@tweet1)
# 編集ページへ遷移する
visit edit_tweet_path(@tweet1)
# すでに投稿済みの内容がフォームに入っていることを確認する
expect(page).to have_field('tweet_image', with: @tweet1.image)
expect(page).to have_field('tweet_text', with: @tweet1.text)
# 投稿内容を編集する
fill_in 'tweet_image', with: "#{@tweet1.image}+編集した画像URL"
fill_in 'tweet_text', with: "#{@tweet1.text}+編集したテキスト"
# 編集してもTweetモデルのカウントは変わらないことを確認する
expect{
find('input[name="commit"]').click
sleep 1
}.to change { Tweet.count }.by(0)
# トップページには先ほど変更した内容のツイートが存在することを確認する(画像)
expect(page).to have_selector ".content_post[style='background-image: url(#{@tweet1.image}+編集した画像URL);']"
# トップページには先ほど変更した内容のツイートが存在することを確認する(テキスト)
expect(page).to have_content("#{@tweet1.text}+編集したテキスト")
end
end
context 'ツイート編集ができないとき' do
it 'ログインしたユーザーは自分以外が投稿したツイートの編集画面には遷移できない' do
# ツイート1を投稿したユーザーでログインする
sign_in(@tweet1.user)
# ツイート2に「編集」へのリンクがないことを確認する
expect(
all('.more')[0].hover
).to have_no_link '編集', href: edit_tweet_path(@tweet2)
end
it 'ログインしていないとツイートの編集画面には遷移できない' do
# トップページにいる
visit root_path
# ツイート1に「編集」へのリンクがないことを確認する
expect(
all('.more')[1].hover
).to have_no_link '編集', href: edit_tweet_path(@tweet1)
# ツイート2に「編集」へのリンクがないことを確認する
expect(
all('.more')[0].hover
).to have_no_link '編集', href: edit_tweet_path(@tweet2)
end
end
end
RSpec.describe 'ツイート削除', type: :system do
before do
@tweet1 = FactoryBot.create(:tweet)
@tweet2 = FactoryBot.create(:tweet)
end
context 'ツイート削除ができるとき' do
it 'ログインしたユーザーは自らが投稿したツイートの削除ができる' do
# ツイート1を投稿したユーザーでログインする
sign_in(@tweet1.user)
# ツイート1に「削除」へのリンクがあることを確認する
expect(
all('.more')[1].hover
).to have_link '削除', href: tweet_path(@tweet1)
# 投稿を削除するとレコードの数が1減ることを確認する
expect{
all('.more')[1].hover.find_link('削除', href: tweet_path(@tweet1)).click
sleep 1
}.to change { Tweet.count }.by(-1)
# トップページにはツイート1の内容が存在しないことを確認する(画像)
expect(page).to have_no_selector ".content_post[style='background-image: url(#{@tweet1.image});']"
# トップページにはツイート1の内容が存在しないことを確認する(テキスト)
expect(page).to have_no_content("#{@tweet1.text}")
end
end
context 'ツイート削除ができないとき' do
it 'ログインしたユーザーは自分以外が投稿したツイートの削除ができない' do
# ツイート1を投稿したユーザーでログインする
sign_in(@tweet1.user)
# ツイート2に「削除」へのリンクがないことを確認する
expect(
all('.more')[0].hover
).to have_no_link '削除', href: tweet_path(@tweet2)
end
it 'ログインしていないとツイートの削除ボタンがない' do
# トップページに移動する
visit root_path
# ツイート1に「削除」へのリンクがないことを確認する
expect(
all('.more')[1].hover
).to have_no_link '削除', href: tweet_path(@tweet1)
# ツイート2に「削除」へのリンクがないことを確認する
expect(
all('.more')[0].hover
).to have_no_link '削除', href: tweet_path(@tweet2)
end
end
end
RSpec.describe 'ツイート詳細', type: :system do
before do
@tweet = FactoryBot.create(:tweet)
end
it 'ログインしたユーザーはツイート詳細ページに遷移してコメント投稿欄が表示される' do
# ログインする
sign_in(@tweet.user)
# ツイートに「詳細」へのリンクがあることを確認する
expect(
all('.more')[0].hover
).to have_link '詳細', href: tweet_path(@tweet)
# 詳細ページに遷移する
visit tweet_path(@tweet)
# 詳細ページにツイートの内容が含まれている
expect(page).to have_selector ".content_post[style='background-image: url(#{@tweet.image});']"
expect(page).to have_content("#{@tweet.text}")
# コメント用のフォームが存在する
expect(page).to have_selector 'form'
end
it 'ログインしていない状態でツイート詳細ページに遷移できるもののコメント投稿欄が表示されない' do
# トップページに移動する
visit root_path
# ツイートに「詳細」へのリンクがあることを確認する
expect(
all('.more')[0].hover
).to have_link '詳細', href: tweet_path(@tweet)
# 詳細ページに遷移する
visit tweet_path(@tweet)
# 詳細ページにツイートの内容が含まれている
expect(page).to have_selector ".content_post[style='background-image: url(#{@tweet.image});']"
expect(page).to have_content("#{@tweet.text}")
# フォームが存在しないことを確認する
expect(page).to have_no_selector 'form'
# 「コメントの投稿には新規登録/ログインが必要です」が表示されていることを確認する
expect(page).to have_content("コメントの投稿には新規登録/ログインが必要です")
end
end
コメント
require 'rails_helper'
RSpec.describe 'コメント投稿', type: :system do
before do
@tweet = FactoryBot.create(:tweet)
@comment = Faker::Lorem.sentence
end
it 'ログインしたユーザーはツイート詳細ページでコメント投稿できる' do
# ログインする
sign_in(@tweet.user)
# ツイート詳細ページに遷移する
visit tweet_path(@tweet)
# フォームに情報を入力する
fill_in 'comment_text', with: @comment
# コメントを送信すると、Commentモデルのカウントが1上がることを確認する
expect{
find('input[name="commit"]').click
sleep 1
}.to change { Comment.count }.by(1)
# 詳細ページにリダイレクトされることを確認する
expect(page).to have_current_path(tweet_path(@tweet))
# 詳細ページ上に先ほどのコメント内容が含まれていることを確認する
expect(page).to have_content @comment
end
end
サインイン(サポートモジュール)
module SignInSupport
def sign_in(user)
visit new_user_session_path
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
find('input[name="commit"]').click
expect(page).to have_current_path(root_path)
end
end
Ruby on Railsまとめ