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:
- Setup - you are using
allowto stub your classes and get responses that you want
- Exercise - you execute tested method
- Verification - you are using
have_receivedto 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
Want to receive useful tips, information about new Ruby gems and articles on a daily basis? Make sure you follow me and say hello!
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.