先日、若手のエンジニアから「RSpecの書き方がよくわからない」と相談されました。具体的には、「テストコードの書き方はわかるけど、RSpecの構文やモック(mock)をどのように活用すればいいのか」という疑問でした。今日は、彼と同じ疑問を持っている人々のために、RSpecの書き方とモックの使う意義について分かりやすく解説します。
1. RSpecの基本的な書き方
まず、非常にシンプルなRuby on Railsのモデル、例えばUserモデルに対するRSpecのテストコードのサンプルを見てみましょう。
require 'rails_helper'
RSpec.describe User, type: :model do
describe '#create' do
context 'when all attributes are valid' do
let(:user) { build(:user) }
it 'is valid with valid attributes' do
expect(user).to be_valid
end
end
context 'when the email is not unique' do
let(:email) { 'test@example.com' }
let!(:user1) { create(:user, email: email) }
let(:user2) { build(:user, email: email) }
it 'is not valid' do
expect(user2).to_not be_valid
end
end
end
end
ここでのポイントは、テストはdescribe
、context
、it
の三つのブロックで構成されることです。「describe」はテストの対象、「context」はテストの状況や条件、「it」は期待する結果を示します。let
はテストデータを定義するために使います。
2. モック(Mock)の意義
次に、モック(Mock)について説明します。モックは、特定のメソッドが呼び出されたときの返り値をテストコード内で固定化するためのテクニックです。モックを使うと、他のコードや外部サービスに依存する部分を切り離してテストすることができます。これによりテストが簡単になり、速度も向上します。
例えば、あるサービスクラスが外部APIからデータを取得するメソッドを持っているとします。テスト時には外部APIに依存せず、ダミーデータを返すようにしたい場合、モックを使うことができます。
ここでは、そのサンプルコードとして UserService
クラスが ExternalApiService
という外部APIを呼び出すと仮定します。
class UserService
def get_user_info(user_id)
response = ExternalApiService.fetch_user_info(user_id)
if response.success?
response.data
else
nil
end
end
end
これをテストするために、 ExternalApiService.fetch_user_info
メソッドの戻り値をモック化します。RSpecではallow
とreceive
を使ってモックを作成します。
require 'rails_helper'
RSpec.describe UserService, type: :service do
describe '#get_user_info' do
let(:user_id) { 1 }
let(:user_service) { UserService.new }
context 'when the external api call is successful' do
let(:mock_response) { double('Response', success?: true, data: 'mock data') }
before do
allow(ExternalApiService).to receive(:fetch_user_info).with(user_id).and_return(mock_response)
end
it 'returns the user info' do
expect(user_service.get_user_info(user_id)).to eq('mock data')
end
end
context 'when the external api call fails' do
let(:mock_response) { double('Response', success?: false) }
before do
allow(ExternalApiService).to receive(:fetch_user_info).with(user_id).and_return(mock_response)
end
it 'returns nil' do
expect(user_service.get_user_info(user_id)).to be_nil
end
end
end
end
このように、モックを使用することで外部APIの成功・失敗に対するテストを独立して行うことができ、テストの実行速度も向上させることが可能です。
RSpecを使ってテストを書く際には、まずは何をテストするべきかをしっかりと理解し、それに合わせたデータを準備します。また、モックを使うことで外部依存性を取り除き、確実かつ高速なテストを実現できます。これらのテクニックを使って、より質の高いテストコードを書くことができるようになることを願っています。