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) endend
このコードでは、before
ブロックで配列のインスタンス変数@numbers
を準備し、それをテスト内で使用しています。before
ブロック内の処理は、各テストケース(it
ブロック)が実行される前に毎回実行されます。
おさらいしておくと、インスタンス変数(@
で始まる変数)はテスト全体から参照可能な変数です。before
の中で準備したデータをテスト内のどこからでも使用することができます。
テストの実行
コードを保存したら、次のコマンドを実行します。
bundle exec rspec spec/before_spec.rb
実行すると、以下のような出力が表示されます。
$ bundle exec rspec spec/before_spec.rbbeforeの基本的な使い方テストの準備をしています 配列のサイズが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_allend
実際のプロジェクトでは、テストデータベースをクリーンな状態に戻したり、特定の条件を設定したりするために、このような処理がよく使われます。
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) endend
このコードでは、各テストケース内で配列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) endend
どちらのコードも同じ結果を得ることができますが、before
を使うことで、初期化処理を1箇所にまとめることができるので、可読性と保守性が向上します。
今回はシンプルな例でしたが、実際のテストでは例えばデータの準備など、いろんな初期化処理が必要になることがあります。そういった場合にbefore
を使うことで、テストコードがすっきりと整理されます。
beforeとletの併用
before
とlet
は併用することができ、それぞれの特徴を活かしたテストコードが書けます。以下のコードで確認してみましょう。
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]) endend
このように、let
でテストデータを定義し、before
でそのデータを使った前処理を行うという組み合わせが可能です。let
の遅延評価とbefore
の自動実行を理解することで、より効率的なテストコードが書けるようになります。
まとめ
RSpecのbefore
フックについて学習しました。主なポイントは以下の通りです。
before
はテスト実行前の準備処理をまとめて記述するための機能- 各テストケースの前に毎回実行される
before
を使うことでコードの重複を避け、可読性と保守性が向上するlet
と併用することで、より柔軟なテスト設計が可能になる
Basicプランでより詳しく学習
この先のコンテンツを読むにはBasicプラン以上が必要です。より詳細な解説、実践的なサンプルコード、演習問題にアクセスして学習を深めましょう。