RSpec spies explained

Ruby on Rails / RSpec

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 expect and 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.