Everyday Rails - RSpecによるRailsテスト入門を読みました。
まずはここから始めようと思ったことをまとめます。
導入方法については以下の記事を参照
RSpecを導入する
用語
spec:テストファイル
example:spec内に書かれているテスト1つ1つ
specの種類
モデルスペック、コントローラースペック、フィーチャースペック、システムスペック、レスポンススペックの5種類があります。特に重要なのは以下の3つです。
モデルスペック:バリデーションや、メソッドの単体テスト。
システムスペック:統合テスト。ユーザーの動作を再現する。手動テストを自動でやる感じ。
昔はフィーチャースペックが使われていたけど、今はこっちが推奨されてるみたいです。
実行に時間がかかります。
レスポンススペック:主に外部APIとの通信をするテストで使われます。
基本文法
まずit '<テスト名>' do ~ end
のブロックの中にデータのセットアップや検証したい項目を書きます。
it 'can have many notes' do
#検証したい内容が入る
end
次にexpect(<検証したい項目>).to <どうなっていて欲しいか>
を書きます。英語の文法と同じ語順なのでとても読みやすくて気に入っています。
it 'can have many notes' do
project = ... #データのセットアップなど
expect(project).to be_valid
end
以下のコマンドでテストを実行します。
$ bin/rspec
.
.
Finished in 3.12 seconds (files took 0.93752 seconds to load)
1 examples, 0 failures
テストデータの生成
各テスト内でUser.new
などで生成しても全然オッケーなのですが、めんどくさいので、FactoryBot
というgemを使います。
$ bin/rails g factory_bot:model user
FactoryBot.define do
factory :user do
name { 'Sample Tarou' }
email { 'test@example.com' }
password { 'mypassword1234' }
end
end
あとはテスト内にFactoryBot.create(:user)
と書くだけで、上記のユーザーのインスタンスを生成することができます。
it 'is valid with a name, email, and password' do
user = FactoryBot.create(:user)
expect(user).to be_valid
end
モデルスペック
$ bin/rails g rspec:model user
require 'rails_helper'
RSpec.describe User, type: :model do
#emailのバリデーションのテスト
it { is_expected.to validate_presence_of :email }
#フルネームを返すnameメソッドのテスト
it "returns a user's full name as a string" do
user = FactoryBot.build(:user, first_name: 'John', last_name: 'Doe')
expect(user.name).to eq 'John Doe'
end
end
システムスペック
$ bin/rails g rspec:system project
require 'rails_helper'
RSpec.feature 'Projects', type: :system do
it 'edits a project' do
sign_in_as user
visit root_path
click_link 'Test Project 1'
click_link 'Edit'
fill_in_project_info('test_updated', 'hogefuga')
click_button 'Update Project'
expect(page).to have_content 'test_updated'
end
it 'creates a new project' do
sign_in_as user
expect do
click_link 'New Project'
fill_in_project_info('Test Project', 'Trying out Capybara')
click_button 'Create Project'
aggregate_failures do
expect(page).to have_content 'Project was successfully created'
expect(page).to have_content 'Test Project'
expect(page).to have_content "Owner: #{user.name}"
end
end.to change(user.projects, :count).by(1)
end
end
リクエストスペック
bin/rails g rspec:request home
require 'rails_helper'
RSpec.describe "Homes", type: :request do
it 'responds successfully' do
get root_path
expect(response).to be_success
expect(response).to have_http_status '200'
end
end
共通のテストデータ
let
を利用することで、1つのファイル内で共通で使うテストデータを作成することができます。
let(:user) { FactoryBot.create(:user) }
it 'is valid with a name, email, and password' do
expect(user).to be_valid
end
it 'is Sample Tarou' do
expect(user).to eq 'Sample Tarou'
end
上の例ではlet
を使いuser
という変数の中にファクトリに定義したユーザーのインスタンスを格納しました。
exampleをグループ分けする
describe
、context
を使って、exampleをグループ分けすることで、可読性をあげることができます。
describe 'search message for a term' do
context 'when a match is found' do
it 'returns notes that match the search term' do
.
.
end
end
context 'when no match is found' do
it 'returns an empty collenction when no result are found' do
.
.
end
it 'returns an empty collenction when serach conditions are invalid' do
.
.
end
end
end
capybara
システムスペックを書く上でcapybara
というgemが必要です。
インストールすると、capybaraのDSL(独自の言語)が使えるようになります。
以下のvisit
、click_link
、fill_in
、click_button
などがDSLです。非常に直感的で気に入っています。
it 'signs in' do
visit root_path
click_link 'Sign In'
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
click_button 'Log in'
expect(page).to have_content 'ようこそ Sample Tarou 様'
end
shoulda-matchers
shoulda-matchers
というgemを使うと、macher(be_validとか)を拡張することができ、特にモデルスペックのバリデーションのチェックで力を発揮します。
it 'is invalid without a first name' do
user = FactoryBot.build(:user, first_name: nil)
user.valid?
expect(user.errors[:first_name]).to include("can't be blank")
end
このバリデーションチェックのテストが
it { is_expected.to validate_presence_of :first_name }
このように1行で検証できるようになります。
デフォルトで使えるマッチャーについては以下の記事を参照
使えるRSpec入門・その2「使用頻度の高いマッチャを使いこなす」
メソッドを切り出す
複数箇所で共通する処理は、メソッドとして切り出すことができる。
ファイルの一番下に書くことが多い。
def go_to_project(name)
visit root_path
click_link name
end
複数のファイルで共通する処理はspecs/support
ディレクトリにファイルを作る。
module LoginSupport
def sign_in_as(user)
visit root_path
click_link "Sign in"
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Log in"
end
end
#以下のように記述することで、各ファイルでincludeする必要がなくなる
RSpec.configure do |config|
config.include LoginSupport
end
明示的に読み込みたい場合には、各ファイル内でinclude LoginSupport
のように記述する。
終わりに
他にもたくさんTIPS的なものがありますが、全部いきなり全部意識するのは無理なので、まずはこの記事にまとめたテクニックでどんどんテストを書いていきます。