mocksを使用した経緯
テストしたいクラスが、S3バケットへの署名付きURLの発行やCSVの生成などのために複数のクラスとの依存関係にあり、単独でテストをする必要があったため使用しました。
参考にした記事
mocksの基本について
ほぼほぼ上記の記事と同じ内容ではありますが、
let(:class_a) { instance_double(ClassA) }
これでClassAのテストダブル(影武者)のclass_aが作成できます。
しかし、このままではこのclass_aはメソッドなどは何も持っていないので情報を付与する必要があります。
allow(ClassA).to receive(:new).and_return(class_a)
allow(class_a).to receive(:upload)
これによって、まず
実際のコードでClassAをnewする際に、先ほど定義したテストダブルの方のclass_aが返されるようになります。
そしてテストダブルの方のclass_aにuploadメソッドを受け取れるように設定しています。(class_aがuploadメソッドを受け取ると何もしないようにモックしています)
またこのように書くことで何かしらの動作を持たせることもできました
allow(class_b).to receive(:call) do
# 何かしらclass_bに持たせたい処理をここに書ける
end
これによって、テスト内でも何かしらの処理を持たせることで、テストダブルに別のテストダブルを呼ばせるなどの動作も取れるようになります。今回はしていませんが。
これらの設定により、実際のコードで
「ClassAのインスタンスをnewしてuploadメソッドを行い処理を行う」
という実際の処理の工程を
「ClassAの実装の詳細を隔離したまま、class_aという代替品にuploadメソッドが届くかどうかの確認をする」
というやり方でテストを行えるようになります。
実際のテストの全体像
コードの全体像は以下のようになりました
require 'rails_helper'
RSpec.describe ClassHoge do
describe '#call' do
subject { 実際の処理.new.call(引数1, 引数2など) }
let(:class_a) { instance_double(ClassA) }
let(:class_b) { instance_double(ClassB) }
before do
allow(ClassA).to receive(:new).and_return(class_a)
allow(class_a).to receive(:upload)
allow(ClassB).to receive(:new).and_return(class_b)
allow(class_b).to receive(:call) do
# 何かしらclass_bに持たせたい処理をここに書ける
end
end
it 'ClassAにhugaして、ほにゃららし、ClassBにhogeしてどうなる' do
subject
expect(class_b).to have_received(:call)
expect(class_a).to have_received(:upload)
end
end
end