この記事は…
- railsで実装済みのいいね機能をRSpecでテスト
- RSpec、Capybaraでのテストの書き方を解説
- rails初心者〜中級者向け
前提
この記事では、railsで実装した「いいね機能」についてRSpecテストをどのように実装するか、を解説していきます。
このため、以下のことを前提として解説を進めます。
いいね機能が実装済みであること
テスト対象の機能が無いと、そもそもテストしても上手くいっているか分かりませんよね。
いいね機能の実装方法は以下の記事で解説しているので、参考にしてください。
機能の実装方法はいくつもありますが、テストとしてやることは基本的に同じなので、使える部分は使っていただく感じがいいと思います。
RSpec導入済みであること
自動テストをやる際に多くの開発者が使う有名なgemなので、解説は不要かなと思います。
この記事では以下のgemを使っていきます。
RSpec テスト
今回、RSpecで行うテストは大きく分けて2つです。
モデル側でのアソシエーションやバリデーションのテスト、Capybara を用いた画面側の表示確認のテストです。
順番に解説していきましょう。
Factory を定義
テストデータ作成のために、Factoryを用いたダミーデータを定義します。
favoriteモデルはアソシエーションを組んでいる関係から、以下の記述のみでOKです。
spec/factories/favorites.rb
FactoryBot.define do
factory :favorite do
user
book
end
end
アソシエーションでUserとBookを参照しているので、それらのコードも載せておきます。
※カラムは自分の環境に合うように読み替えてください。
spec/factories/users.rb
FactoryBot.define do
factory :user do
name { Faker::Lorem.characters(number: 10) }
email { Faker::Internet.email }
password { 'password' }
password_confirmation { 'password' }
end
end
spec/factories/books.rb
FactoryBot.define do
factory :book do
title { Faker::Lorem.characters(number: 5) }
user
end
end
モデルのテスト
モデル側のテストは、バリデーションとアソシエーションの確認のみのため、いたってシンプルです。
まず先にテストコードを紹介しておきます。
spec/models/favorite_spec.rb
require 'rails_helper'
RSpec.describe 'Favoriteモデルのテスト', type: :model do
describe 'バリデーションのテスト' do
subject { favorite.valid? }
let!(:other_favorite) { create(:favorite) }
let(:favorite) { build(:favorite) }
context '1User 1Book 1いいね' do
it 'あるUserが同じBookにいいね出来ないこと' do
favorite.user = other_favorite.user
favorite.book = other_favorite.book
is_expected.to eq false
end
end
end
describe 'アソシエーションのテスト' do
context 'Userモデルとの関係' do
it 'N:1となっている' do
expect(Favorite.reflect_on_association(:user).macro).to eq :belongs_to
end
end
context 'Bookモデルとの関係' do
it 'N:1となっている' do
expect(Favorite.reflect_on_association(:book).macro).to eq :belongs_to
end
end
end
end
最初の let の部分で、Factoryで定義したfavoriteを使っており、2つのfavoriteレコードを作成しています。
2つ作る理由は、バリデーションで重複レコードをテストするためです。
バリデーションのテストは少し複雑で、1ユーザーが同じBookに何回も”いいね”出来ないように制御しているか、のテストです。
favoriteモデルでは以下のようにバリデーションを組んでいるので、同じBookに複数回いいねすることが出来ないようになっています。
app/models/favorite.rb
class Favorite < ApplicationRecord
belongs_to :user
belongs_to :book
validates :book_id, uniqueness: {scope: :user_id}
end
Capybara でのテスト
今回テスト対象となる”いいね”のリンクは、以下のようなパーシャルビューとなっています。
app/views/favorites/_thumbs.rb
<%= link_to book_favorite_path(book), method: :post, remote:true, id: 'thumbs-up' do %>
<i class="far fa-thumbs-up"></i>
<% end %>
<%= book.favorite.size %>
Capybara でJSは無理
いいね機能はだいたい非同期通信でやり取りするような実装になっているので、JSが絡むと単純にCapybaraだけではテスト出来ないという問題があります。
なので、以下のようなテストコードを実行しても画面が変化しないので、意図した結果が得られません。
find_all('i.far')[0].click
ではどうすればいいかと言うと、seleniumを使うことがCapybara公式チームより推奨されています。
selenium 入れたくない
selenium は、ざっくり言うとブラウザを立ち上げてプログラムがクリック操作などを実際に行うようにするためのgemです。
まあユーザー操作をプログラムが代わりにやってくれるヤツということです。
しかし、“いいね”ボタンを押すためだけに色々追加するのはメンドウだし、コスパも悪いので、ちょっと工夫しましょう。
工夫してseleniumを回避
どうにかCapybaraだけでテストできないものか考え、思いついたのが先に”いいね済”にしてしまう方法でした。
“未いいね”状態のテストもしたいので、Bookレコードを2つ作成し、1つだけ”いいね済”にすれば、両方のテストができます。
というわけで、出来上がったテストがこちらです。
spec/system/capybara_spec.rb
require 'rails_helper'
describe 'book一覧画面のテスト' do
let(:user) { create(:user) }
let!(:other_user) { create(:user) }
let!(:book) { create(:book, user: user) }
let!(:other_book) { create(:book, user: other_user) }
let!(:favorite) { create(:favorite, book: book, user: user) } # 1ついいね済にしておく
before do
visit books_path
end
context 'いいね確認' do
it 'リンクが諸々正しい' do
expect(page).to have_link '', href: book_favorite_path(book) #リンクが正しい
expect(page).to have_css('i.far') #いいねの表示
expect(page).to have_css('i.fas') #いいね済の表示
end
end
end
最後にrspecのコマンドを実行して結果を確認してみましょう!
bundle exec rspec spec/system --format documentation
まとめ
JSが絡むテストはクセがあることがわかりました。
seleniumを入れれば将来的にもテストが楽になるかもしれませんね。(開発者もユーザーテストするからカバー出来るよねって話もありますが笑)
とはいえ、今回のような方法で、JSを動かさずとも表示内容のテストを組めるので、いろんな所で応用して使っていきたいものですね。
コメント