contextでテスト条件を整理しよう

学習の目標

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

  • RSpecのcontextの基本概念と役割を理解する
  • テストケースを条件ごとに分類する方法を習得する
  • describeとcontextの適切な使い分けを学ぶ
  • 会員種別や状態に応じたテストケースの分類方法を習得する
  • テスト要件の追加や変更に柔軟に対応できる保守性の高いテストコード設計を学ぶ

はじめに

前回の章では、describeを使ってテストをグループ化する方法を学びました。今回は、テストをさらに細かく整理するためのcontextについて学んでいきましょう。

contextは、describeの中で使用し、特定の条件や状況に応じてテストを区分けするための機能です。これにより、テストコードがより読みやすく、意図が伝わりやすくなります。

contextの役割と使い所

私たちが開発するアプリケーションには、様々な条件によって動作が変わる場面がよくあります。例えば次のような状況です。

  • ユーザーがログインしているかどうかで表示内容が変わる
  • 会員種別(無料/有料)によって利用できる機能が異なる
  • 商品の在庫状況によって購入ボタンの挙動が変わる

このような条件ごとの動作の違いをテストする場合、contextを使うと非常に分かりやすくなります。「この条件の時はこう動作する」「別の条件の時はこう動作する」という形でテストを整理できるのです。

例えば、ユーザーのログイン状態によるテストでは、次のように整理できます。

  • 「ログインしている場合」のテスト
  • 「ログアウトしている場合」のテスト

また、会員種別による機能の違いを整理する場合は次のようになります。

  • 「有料会員である場合」のテスト
  • 「無料会員である場合」のテスト

このように条件ごとにテストをグループ化することで、テストコードを読む人は「どのような状況で、どのような動作を期待しているのか」が一目で理解できるようになります。

テスト対象のプログラムを準備する

それでは、具体的な例を通してcontextの使い方を学んでいきましょう。まずはテスト対象となる簡単な会員ポイント計算プログラムを作成します。

point_calculator.rbというファイルを作成し、以下のコードを記述します。

# point_calculator.rb
class PointCalculator
def calculate_points(amount, membership_type)
if membership_type == 'gold'
amount * 2
else
amount
end
end
end

このコードは、eコマースサイトでよくある会員ポイントの計算を模したシンプルなクラスです。次のような仕様になっています。

  • 購入金額(amount)と会員種別(membership_type)に基づいてポイントを計算します
  • ゴールド会員の場合は購入金額の2倍のポイントを獲得できます
  • 一般会員の場合は購入金額と同じポイントを獲得します

こういった会員種別による挙動の違いは、実際のアプリケーション開発でもよく見られるパターンです。このような条件分岐がある処理は、contextを使ってテストを整理するのに最適です。

contextを使ったテストの構造化

それでは、上記のPointCalculatorクラスに対するテストを記述していきましょう。ここでcontextを活用して、会員種別ごとにテストを分類します。

spec/point_calculator_spec.rbというファイルを作成し、以下のコードを記述してください。

# spec/point_calculator_spec.rb
require_relative '../point_calculator'
RSpec.describe PointCalculator do
describe '#calculate_points' do
context 'ゴールド会員の場合' do
it 'ポイントが2倍になる' do
calculator = PointCalculator.new
expect(calculator.calculate_points(100, 'gold')).to eq(200)
end
end
context '一般会員の場合' do
it 'ポイントはそのまま' do
calculator = PointCalculator.new
expect(calculator.calculate_points(100, 'regular')).to eq(100)
end
end
end
end

まずこのコードを書き込んだら、ターミナルで次のコマンドを実行してテストを走らせてみましょう。

bundle exec rspec spec/point_calculator_spec.rb

テストが正常に実行されると、以下のような結果が表示されるはずです。

PointCalculator
#calculate_points
ゴールド会員の場合
ポイントが2倍になる
一般会員の場合
ポイントはそのまま
Finished in 0.00354 seconds (files took 0.07539 seconds to load)
2 examples, 0 failures

テストコードの構造を理解する

それでは、このcontextを使ったテストコードの構造を詳しく見ていきましょう。

  1. 最上位のdescribeでは、テスト対象のクラス(PointCalculator)を指定しています
  2. 次のdescribeでは、テスト対象のメソッド(#calculate_points)を指定しています
    • メソッド名の前に#をつけるのは、インスタンスメソッドであることを示す慣習です
  3. contextで会員種別ごとの条件を分類しています
    • 「ゴールド会員の場合」と「一般会員の場合」という2つの条件を明示しています
  4. context内でitを使って具体的なテストケースを記述しています
    • 各条件下での期待される動作を自然な言葉で表現しています

この階層構造により、「何をテストしているのか」「どういう条件でテストしているのか」「何を期待しているのか」が非常に明確になります。テストコードを読む人は、テストの意図を簡単に理解することができます。

describeとcontextの違いと使い分け

ここで疑問に思うかもしれません。「describecontextは似ているけど、どう使い分ければいいの?」

実は、describecontextは機能的には同じです。どちらもテストをグループ化するためのブロックで、どちらを使っても動作に違いはありません。違いは主に意味的な区別です。

一般的には、以下のような使い分けをします。

  • describe: 「何をテストするか」を示す(クラス、メソッド、機能など)
  • context: 「どういう状況・条件でテストするか」を示す

この使い分けに従うことで、テストコードの可読性が向上し、他の開発者にとっても理解しやすいテストになります。

まとめ

本章では、RSpecのcontextについて学習しました。以下の内容をマスターできたことと思います。

  • contextは条件ごとにテストを整理するための機能である
  • describeが「何をテストするか」を示すのに対し、contextは「どういう条件でテストするか」を示す
  • contextを使うことで、テストコードの可読性と意図の明確性が向上する
  • 条件分岐を含む処理のテストに特に有効である

contextを適切に使うことで、テストコードはより読みやすく、保守しやすくなります。また、テストから仕様が読み取れるようになるため、ドキュメントとしての役割も果たすことができます。

次回は、テストの期待値を記述するexpectとマッチャーについて学習します。よりきめ細かなテスト条件の指定方法を理解することで、さらに表現力の高いテストが記述できるようになります。

このセクションは有料サブスクリプションへの登録、またはログインが必要です。完全なコンテンツにアクセスするには、料金ページ(/pricing)をご覧ください。購入済みの場合は、ログインしてください。

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

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

作成者:とまだ
Previous
describeでテストを構造化しよう