ドキュメント形式での出力
--format documentation
この設定を適用すると、実行中の各テストケースの名前が出力されるため、どのテストが成功したか、または失敗したかを一目で把握することができます。
これにより、テストの結果が直感的に理解しやすくなり、問題の特定と修正が迅速に行えるようになります。
設定前
設定を加える前は、テストの進捗が.
やF
で表示され、どのテストが失敗したのかすぐにはわかりません。
....F.....
設定後
--format documentation
を設定すると、各テストケースの説明が表示されるため、どのテストが成功し、どのテストが失敗したのかが一目でわかります。
計算
二つの数字を加算する
2と2を渡した時
4と等しいこと
-1と-1を渡した時
-2と等しいこと
二つの数字を減算する
2と2を渡した時
0と等しいこと
二つの数字を乗算する
3と3を渡した時
9と等しいこと
二つの数字を除算する
9と3を渡した時
(FAILED - 1)
ベストプラクティスに沿う
Rspecの書き方については、下記のベストプラクティスに従うと読みやすくなります。
もちろんです。以下に日本語の例を挙げます。
期待する結果を能動系で明示的に記述する
不適切な例:
it 'メールアドレスを持って保存されるか' do
user = User.new(email: 'test@example.com')
expect(user.save).to be_truthy # 能動系ではなく、結果が漠然としている
end
適切な例:
it '有効なメールアドレスを持つユーザーは保存される' do
user = User.new(email: 'test@example.com')
expect(user).to be_valid # 能動的で明確な期待
end
テスト結果を動詞を使って説明する
不適切な例:
it 'メールアドレスの存在' do
user = User.new(email: nil)
expect(user).not_to be_valid # 何が期待されるか不明確
end
適切な例:
it 'メールアドレスがない場合、ユーザーは拒否される' do
user = User.new(email: nil)
expect(user).not_to be_valid # 明確な行動と結果
end
チェックする結果をテスト一つにつき一個だけにする
不適切な例:
it '有効なメールアドレスとパスワードでユーザーが作成される' do
user = User.new(email: 'test@example.com', password: 'password')
expect(user).to be_valid
expect(user.email).to eq('test@example.com') # 複数のアサーション
expect(user.password).to eq('password')
end
適切な例:
# ユーザー作成のテスト
it '有効なメールアドレスを持つユーザーは保存される' do
user = User.new(email: 'test@example.com', password: 'password')
expect(user).to be_valid
end
# メールアドレスの検証
it 'ユーザーのメールアドレスが正しく設定されている' do
user = User.create(email: 'test@example.com', password: 'password')
expect(user.email).to eq('test@example.com')
end
# パスワードの検証
it 'ユーザーのパスワードが正しく設定されている' do
user = User.create(email: 'test@example.com', password: 'password')
expect(user.password).to eq('password')
end
describeとcontextの使い分け
この二つにもそれぞれ書く内容には定義があります。
describeの使い方
describe
は主に、テストの対象となるメソッドやクラス、機能など「何をテストしているのか」をグルーピングするために使います。
RSpec.describe User do
describe '#name' do
it 'ユーザーのフルネームを返す' do
user = User.new(first_name: '太郎', last_name: '山田')
expect(user.name).to eq '山田 太郎'
end
end
end
contextの使い方
context
は「どのような状況や前提条件でテストをしているのか」を説明するときに使います。context
はdescribe
の中でさらに状況を細分化して使われることが多いです。
RSpec.describe User do
describe '#name' do
context '名前と姓が両方存在する場合' do
it 'フルネームをスペースで区切って返す' do
user = User.new(first_name: '太郎', last_name: '山田')
expect(user.name).to eq '山田 太郎'
end
end
context '姓のみが存在する場合' do
it '姓のみを返す' do
user = User.new(last_name: '山田')
expect(user.name).to eq '山田'
end
end
end
end
itとspecifyの使い分け
it
、specify
、example
などのエイリアスがあります。
下記が参考になります。
コントローラのテストは時代遅れ
コントローラのテストは、最近では非推奨とされています。
代わりとしてリクエストスペックの使用が推奨されています。
リクエストスペックを書くタイミング
リクエストスペックは基本的に外部のAPIとの通信をテストするさいに使い、基本的はモデルとシステムテストのみを書くようにします。
これはこのEveryday Railsの著者の伊藤淳一さんが答えた動画などが記載されている記事があります。
システムテストにおけるドライバの適切な使い分け
システムテストを高速かつ効率的に行うには、テストの内容に応じて適切なドライバを選択することが鍵です。主に、「JavaScriptを使うか否か」によってドライバを使い分けます。
-
JavaScriptを使用しないテスト:これらは
rack_test
ドライバで行います。rack_test
はJavaScriptの実行をサポートしていない代わりに、非常に高速でテストを実行できます。HTMLコンテンツの生成やフォームの送信など、シンプルな操作をテストする際に適しています。 -
JavaScriptを使用するテスト:
selenium_chrome
ドライバを使用します。これにより、JavaScriptの動作を含むユーザーインタラクションをエミュレートできますが、テストの実行速度は低下する可能性があります。
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
end
end
js: true
タグを付けることで、JavaScriptが必要なテストケースでselenium_chrome
を使うように指定します。
さらに、selenium_chromeheadless
を使用すると、ブラウザウィンドウを開かずにテストを実行することができるため、テストの実行速度が向上します。テストの大部分が通過するようになったら、ヘッドレスモードへの移行を検討しましょう。
DRYにするためにサポートモジュールを活用する
例えばログイン処理を各テストに何度も書くのはDRY原則に反しています。
メソッド化してもいいですが、よりファイル内のコードを減らすために、サポートモジュールに切り出します。
module LoginMacros
def login_as(user)
visit root_path
find('.btn-primary.btn-lg', text: 'ログイン').click
fill_in 'メールアドレス', with: user.email
fill_in 'パスワード', with: 'password'
click_button 'ログイン'
expect(page).to have_content "ログインに成功しました"
end
end
失敗のテストはまとめる
Rspecは通常、一つの検証が失敗するとそこで停止します。
ですが、aggregate_failures
で囲むと一つ検証(エクスペクテーション)が失敗しても続けて検証します。
aggregate_failuresを使わない場合
RSpec.describe 'Calculator' do
it 'correctly performs arithmetic' do
calculator = Calculator.new
expect(calculator.add(2, 2)).to eq(4) # 成功
expect(calculator.subtract(5, 3)).to eq(3) # 失敗
expect(calculator.multiply(3, 3)).to eq(9) # 実行されず
end
end
aggregate_failuresを使用した場合
RSpec.describe 'Calculator' do
it 'correctly performs arithmetic', :aggregate_failures do
calculator = Calculator.new
expect(calculator.add(2, 2)).to eq(4) # 成功
expect(calculator.subtract(5, 3)).to eq(2) # 失敗
expect(calculator.multiply(3, 3)).to eq(9) # 成功
expect(calculator.divide(9, 3)).to eq(3) # 成功
end
end
モデルバリデーションテストの効率化
shoulda-matchers
は、モデルバリデーションのテストコードを簡潔に記述することを可能にするgemです。これにより、一般的なバリデーションのテストケースをわずか数行のコードで表現することができます。
導入方法
まず、Gemfileに以下を追加してshoulda-matchers
gemをインストールします。
group :test do
gem 'shoulda-matchers'
end
そして、bundle install
を実行してgemをプロジェクトにインストールします。
設定の追加
インストール後、RSpecにshoulda-matchers
を認識させるための設定を追加する必要があります。spec/rails_helper.rb
またはspec_helper.rb
に以下の設定を追加してください。
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
end
この設定を行うことで、RSpecはshoulda-matchers
を利用する準備が整います。
実装例の確認
実際のコードへの適用例を見ることで、shoulda-matchers
の利便性をより深く理解することができます。下記のGitHubリンクでは、私が行った実装を通じて、実際にどのようにshoulda-matchers
がテストコードをシンプルにするかを示しています。
GitHub Commit: shoulda-matchersの導入
特定のテストだけ実行させる
focus: true
をテストに追加して、特定のテストケースに焦点を当ててRSpecを実行したい場合は、以下のコマンドを使用します。
rspec --tag focus
これにより、開発中に特定の機能やバグ修正に集中したテストを簡単に行うことができます。
アップロードしたファイルを自動的に削除
下記を設定すると自動で削除されるようになります。
config.after(:suite) do
FileUtils.rm_rf(ActiveStorage::Blob.service.root)
end
アドバイス
- まずはコードを書いてからテストを書いてもよい。ただし、セットで行うこと
- システムスペックから描き始められるようになること
- テストする時間を作ること
- 常にシンプルであること