はじめに
Railsでアプリケーションを開発する際、品質を担保するためにテストは欠かせません。その中でも、RSpecは多くの開発者に愛用されているテストフレームワークです。
今回は、RSpecを使ったRailsアプリケーションのテスト方法について、実践的な視点から解説していきます。
システムスペック
Capybara
RSpecのシステムスペックでは、Capybaraを使用してブラウザベースのエンドツーエンド(E2E)テストを実行します。Capybaraは、ウェブアプリケーションのユーザーインターフェースをテストするためのツールで、実際のユーザーが行う操作をシミュレートします。
まず、rails_helper.rb
にCapybaraの設定を追加します。
spec/rails_helper.rb
require 'capybara/rspec'
RSpec.configure do |config|
config.before(:each, type: :system) do
driven_by(:rack_test)
end
config.before(:each, type: :system, js: true) do
driven_by(:selenium_chrome_headless)
end
end
ここでは、ユーザーがサインアップ、ログイン、ログアウトする一連の操作をテストするシステムスペックを示します。
まず、簡単なユーザーコントローラーと対応するビューを用意します。
app/controllers/users_controller.rb
class UsersController < ApplicationController
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
session[:user_id] = @user.id
redirect_to root_path, notice: 'User was successfully created.'
else
render :new
end
end
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
end
app/views/users/new.html.erb
<h1>Sign Up</h1>
<%= form_with(model: @user, local: true) do |form| %>
<div>
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<div>
<%= form.label :email %>
<%= form.text_field :email %>
</div>
<div>
<%= form.label :password %>
<%= form.password_field :password %>
</div>
<div>
<%= form.label :password_confirmation %>
<%= form.password_field :password_confirmation %>
</div>
<div>
<%= form.submit "Sign Up" %>
</div>
<% end %>
次に、RSpecとCapybaraを使用してこれらの操作をテストします。
spec/system/users_spec.rb
require 'rails_helper'
RSpec.describe 'User management', type: :system do
describe 'sign up' do
it 'allows a user to sign up' do
visit new_user_path
fill_in 'Name', with: 'Alice'
fill_in 'Email', with: 'alice@example.com'
fill_in 'Password', with: 'password'
fill_in 'Password confirmation', with: 'password'
click_button 'Sign Up'
expect(page).to have_content('User was successfully created.')
expect(page).to have_content('Welcome, Alice')
end
end
describe 'log in and log out' do
let!(:user) { User.create!(name: 'Alice', email: 'alice@example.com', password: 'password') }
it 'allows a user to log in and log out' do
visit login_path
fill_in 'Email', with: 'alice@example.com'
fill_in 'Password', with: 'password'
click_button 'Log In'
expect(page).to have_content('Welcome, Alice')
click_link 'Log Out'
expect(page).to have_content('Logged out successfully')
end
end
end
- ユーザーのサインアップ
visit new_user_path
でサインアップページにアクセスします。- フォームにユーザー情報を入力し、
click_button 'Sign Up'
でフォームを送信します。 - サインアップが成功し、ユーザーが作成されたことを確認します。
- ユーザーのログインとログアウト
- 事前に
let!(:user)
でユーザーを作成しておきます。 visit login_path
でログインページにアクセスします。- フォームにログイン情報を入力し、
click_button 'Log In'
でフォームを送信します。 - ログインが成功し、ユーザーがログイン状態であることを確認します。
click_link 'Log Out'
でログアウトし、ログアウトが成功したことを確認します。
- 事前に
ユーザーインターフェースのテスト
RSpecのシステムスペックにおけるユーザーインターフェースのテストでは、主にCapybaraを使用してブラウザの振る舞いをシミュレートし、ユーザーが行う操作や期待される表示を確認します。
例として、以下のようなコントローラーとビューを考えます。
app/controllers/books_controller.rb
class BooksController < ApplicationController
def index
@books = Book.all
end
def show
@book = Book.find(params[:id])
end
# 他のアクション(create, update, delete)も同様に実装する
end
app/views/books/index.html.erb
<h1>Books</h1>
<ul>
<% @books.each do |book| %>
<li><%= link_to book.title, book_path(book) %></li>
<% end %>
</ul>
<%= link_to 'New Book', new_book_path %>
システムスペックでは、実際にブラウザをシミュレートして操作し、期待される振る舞いを確認します。
spec/system/books_spec.rb
require 'rails_helper'
RSpec.describe 'Books', type: :system do
describe 'index page' do
it 'displays a list of books' do
books = create_list(:book, 3) # 3つのダミーデータを作成する
visit books_path
books.each do |book|
expect(page).to have_link(book.title, href: book_path(book))
end
end
end
describe 'show page' do
it 'displays the book details' do
book = create(:book, title: 'Sample Book', author: 'Sample Author')
visit book_path(book)
expect(page).to have_content('Sample Book')
expect(page).to have_content('Sample Author')
end
end
describe 'creating a new book' do
it 'adds a new book to the list' do
visit new_book_path
fill_in 'Title', with: 'New Book'
fill_in 'Author', with: 'New Author'
click_button 'Create Book'
expect(page).to have_content('New Book')
expect(page).to have_content('New Author')
end
end
describe 'editing a book' do
it 'updates the book details' do
book = create(:book, title: 'Old Title', author: 'Old Author')
visit edit_book_path(book)
fill_in 'Title', with: 'Updated Title'
fill_in 'Author', with: 'Updated Author'
click_button 'Update Book'
expect(page).to have_content('Updated Title')
expect(page).to have_content('Updated Author')
end
end
describe 'deleting a book' do
it 'removes the book from the list' do
book = create(:book, title: 'Book to Delete')
visit books_path
within "#book_#{book.id}" do
click_link 'Delete'
end
expect(page).not_to have_content('Book to Delete')
end
end
end
- インデックスページのテスト
visit books_path
で本の一覧ページにアクセスします。- 各本のタイトルがリンクとして表示されていることを確認します。
- 詳細ページのテスト
visit book_path(book)
で本の詳細ページにアクセスします。- 本のタイトルと著者が表示されていることを確認します。
- 新規作成のテスト
visit new_book_path
で新規作成ページにアクセスします。- フォームにタイトルと著者を入力し、
click_button 'Create Book'
で本を作成します。 - 作成した本のタイトルと著者が表示されていることを確認します。
- 編集のテスト
visit edit_book_path(book)
で本の編集ページにアクセスします。- フォームに新しいタイトルと著者を入力し、
click_button 'Update Book'
で本を更新します。 - 更新した本のタイトルと著者が表示されていることを確認します。
- 削除のテスト
visit books_path
で本の一覧ページにアクセスします。- 削除リンクをクリックして、本が一覧から削除されていることを確認します。
JavaScriptを含む動的な振る舞いのテスト
RSpecのシステムスペックでは、JavaScriptを含む動的な振る舞いをテストする場合、通常はブラウザを制御するためにCapybaraとSeleniumを組み合わせて使用します。
まず、CapybaraとSeleniumを使うための設定をrails_helper.rb
に追加します。
spec/rails_helper.rb
require 'capybara/rspec'
require 'selenium-webdriver'
Capybara.register_driver :selenium_chrome_headless do |app|
options = Selenium::WebDriver::Chrome::Options.new(args: %w[headless disable-gpu])
Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
end
RSpec.configure do |config|
config.before(:each, type: :system, js: true) do
driven_by :selenium_chrome_headless
end
end
capybara/rspec
とselenium-webdriver
をrequire
し、Seleniumのドライバーを設定します。:selenium_chrome_headless
ドライバーを登録し、テスト実行時にヘッドレスChromeを使用するように設定します。
以下は、JavaScriptを含む動的な振る舞いを持つフォームのテスト例です。
spec/system/books_spec.rb
require 'rails_helper'
RSpec.describe 'Books', type: :system, js: true do
describe 'creating a new book asynchronously' do
it 'adds a new book to the list without page reload' do
visit new_book_path
fill_in 'Title', with: 'New Book'
fill_in 'Author', with: 'New Author'
click_button 'Create Book'
# フォームの送信後、非同期でリストに新しい本が追加されることを確認する
within '#books-list' do
expect(page).to have_content('New Book')
expect(page).to have_content('New Author')
end
end
end
end
RSpec.describe
ブロックでtype: :system
,js: true
を指定し、システムスペックでJavaScriptを含む動的な振る舞いをテストします。describe
ブロック内にテストケースを定義し、it
ブロックでテストの内容を記述します。visit
メソッドで対象のページにアクセスします。この例では、新規作成ページにアクセスしています。fill_in
メソッドでフォームの入力を行います。click_button
メソッドでフォームを送信します。within
ブロック内で、非同期で更新された部分のコンテンツが正しく表示されていることを確認します。
まとめ
RSpecを使いこなすことで、Railsアプリケーションの品質を大幅に向上させることができます。ただし、テストの書きすぎには注意が必要です。重要な機能や複雑なロジックに焦点を当て、バランスの取れたテスト戦略を立てることが大切です。
また、CIツールと組み合わせることで、継続的にテストを実行し、問題を早期に発見することができます。例えば、GitHubActionsを使えば、プッシュやプルリクエスト時に自動的にテストを実行できます。
Railsアプリケーション開発において、RSpecは強力な味方となります。ぜひ、日々の開発に取り入れて、より堅牢なアプリケーション作りを目指してください。