beforeでテスト実行前の処理を共通化しよう

学習の目標

本章では、以下の内容を学習します。

  • beforeフックの基本的な使い方を理解する
  • テスト実行前の準備処理を効率的に管理する方法を習得する
  • beforeのスコープの違いと適切な使い分けを学ぶ
  • letとbeforeを組み合わせた効果的なテスト設計を理解する

はじめに

RSpecのbeforeについて説明していきます。beforeは、テストを実行する前の準備処理を行うための重要な機能です。

テストの前処理を効率的に管理することができ、テストに必要な初期化処理をまとめて記述することが可能になります。これにより、コードの重複を避け、テストの可読性と保守性を高めることができます。

前回学習したlet!(即時評価)でもテストデータを事前に準備することができましたが、beforeを使用することでより柔軟な前処理が可能になります。

では、beforeを使うことで何が嬉しいのかを見ていきましょう。

  • テストの前に共通の処理をまとめて記述できる
  • 各テストケースで同じ初期化処理を書く必要がなくなる
  • テストの可読性が向上し、保守性も高まる
  • スコープを使い分けることで、より細かな実行制御が可能になる

究極を言えば、beforeを使わなくてもテストは書けます。しかし、beforeを使うことでテストコードがシンプルになり、可読性が向上します。特に、複数のテストケースで同じ初期化処理が必要な場合には、beforeを使うことを強くお勧めします。

基本的な使い方

では、実際のコードを見ながら学んでいきましょう。

まず、beforeの基本を確認します。次のコードをspec/before_spec.rbに記述してください。

RSpec.describe 'beforeの基本的な使い方' do
before do
@numbers = [1, 2, 3]
puts 'テストの準備をしています'
end
it '配列のサイズが3であることを確認する' do
expect(@numbers.size).to eq(3)
end
it '配列に1が含まれていることを確認する' do
expect(@numbers).to include(1)
end
end

このコードでは、beforeブロックで配列のインスタンス変数@numbersを準備し、それをテスト内で使用しています。beforeブロック内の処理は、各テストケース(itブロック)が実行される前に毎回実行されます。

おさらいしておくと、インスタンス変数(@で始まる変数)はテスト全体から参照可能な変数です。beforeの中で準備したデータをテスト内のどこからでも使用することができます。

テストの実行

コードを保存したら、次のコマンドを実行します。

bundle exec rspec spec/before_spec.rb

実行すると、以下のような出力が表示されます。

$ bundle exec rspec spec/before_spec.rb
beforeの基本的な使い方
テストの準備をしています
配列のサイズが3であることを確認する
テストの準備をしています
配列に1が含まれていることを確認する
Finished in 0.00546 seconds (files took 0.12664 seconds to load)
2 examples, 0 failures

出力結果から、各テストケースの実行前にbeforeブロックが実行されていることが確認できます。これは、putsメソッドの出力が各テストの前に表示されていることからわかります。

ここで重要な点は、beforeは各テストが実行される前に「毎回」実行されるという特徴です。これは前回学習したletが「必要になった時に」実行されるのとは異なります。

この特徴を活かして、例えばデータベースの初期化など、各テストの前に必ず実行したい処理を記述することができます。

# 例:毎回ユーザーのデータを初期化する事前準備
before do
UserRecord.delete_all
end

実際のプロジェクトでは、テストデータベースをクリーンな状態に戻したり、特定の条件を設定したりするために、このような処理がよく使われます。

beforeを使う場合と使わない場合の比較

それでは、beforeを使う場合と使わない場合の違いを見てみましょう。以下のコードは、beforeを使わない場合の例です。

RSpec.describe 'beforeを使わない場合' do
it '配列のサイズが3であることを確認する' do
numbers = [1, 2, 3]
puts 'テストの準備をしています'
expect(numbers.size).to eq(3)
end
it '配列に1が含まれていることを確認する' do
numbers = [1, 2, 3]
puts 'テストの準備をしています'
expect(numbers).to include(1)
end
end

このコードでは、各テストケース内で配列numbersを初期化しています。これにより、同じ初期化処理が2回繰り返されてしまっています。 このように、beforeを使わない場合は、同じ処理を繰り返す必要があり、コードの重複が発生します。これに対して、beforeを使うことで、初期化処理を1箇所にまとめることができ、可読性と保守性が向上します。

RSpec.describe 'beforeを使う場合' do
before do
@numbers = [1, 2, 3]
puts 'テストの準備をしています'
end
it '配列のサイズが3であることを確認する' do
expect(@numbers.size).to eq(3)
end
it '配列に1が含まれていることを確認する' do
expect(@numbers).to include(1)
end
end

どちらのコードも同じ結果を得ることができますが、beforeを使うことで、初期化処理を1箇所にまとめることができるので、可読性と保守性が向上します。

今回はシンプルな例でしたが、実際のテストでは例えばデータの準備など、いろんな初期化処理が必要になることがあります。そういった場合にbeforeを使うことで、テストコードがすっきりと整理されます。

beforeとletの併用

beforeletは併用することができ、それぞれの特徴を活かしたテストコードが書けます。以下のコードで確認してみましょう。

RSpec.describe 'beforeとletの併用' do
let(:numbers) { [1, 2, 3] }
before do
puts "テストを実行します"
@doubled_numbers = numbers.map { |n| n * 2 }
end
it '元の配列が変更されていないこと' do
expect(numbers).to eq([1, 2, 3])
end
it '各要素が2倍になっていること' do
expect(@doubled_numbers).to eq([2, 4, 6])
end
end

このように、letでテストデータを定義し、beforeでそのデータを使った前処理を行うという組み合わせが可能です。letの遅延評価とbeforeの自動実行を理解することで、より効率的なテストコードが書けるようになります。

まとめ

RSpecのbeforeフックについて学習しました。主なポイントは以下の通りです。

  • beforeはテスト実行前の準備処理をまとめて記述するための機能
  • 各テストケースの前に毎回実行される
  • beforeを使うことでコードの重複を避け、可読性と保守性が向上する
  • letと併用することで、より柔軟なテスト設計が可能になる
このセクションは有料サブスクリプションへの登録、またはログインが必要です。完全なコンテンツにアクセスするには、料金ページ(/pricing)をご覧ください。購入済みの場合は、ログインしてください。

Basicプランでより詳しく学習

この先のコンテンツを読むにはBasicプラン以上が必要です。より詳細な解説、実践的なサンプルコード、演習問題にアクセスして学習を深めましょう。

作成者:とまだ
Previous
letでテストデータを効率的に管理しよう