RSpec spies are a combination of mocks and stubs. If you are not familiarized with them you can check previous post about this topic: RSpec stubs explained.
You can divide spies into a three-step flow:
1. Setup – you are using
allow to stub your classes and get responses that you want
2. Exercise – you execute tested method
3. Verification – you are using
have_received to test if your code meet expectations
Let’s see implementation on a sample class.
class UserService def initialize(user:, name:) @user = user @name = name end def save_name name_service = NameService.new(name: name) user.update_attribute(:name, name_service.get_name(format: :short)) end private attr_reader :user, :name end
require 'spec_helper' describe UserService do describe "#save_name" do # Setup name_service = instance_double(NameService, get_name: double) user = instance_double(User, update_attribute: double) short_name = 'Nick' name = 'Nick Martin' allow(NameService).to receive(:new).with(name: name).and_return(name_service) allow(name_service).to receive(:get_name).with(format: :short).and_return(short_name) allow(user).to receive(:update_attribute).with(:name, short_name) # Exercise user_service = UserService.new(user: user, name: name) user_service.save_name # Verification expect(name_service).to have_received(:get_name).with(format: :short) expect(user).to receive(:update_attribute).with(:name, short_name) end end
Of course, you can do it faster but in using spies it’s all about clarity, especially in more complicated cases, so that’s why we divided the whole test into separated sections.