DEV Community

loading...

Explaining RSpec instance_double in 2 minutes

vasily profile image Vasily Polovnyov Updated on ・1 min read

Let's take the Notifications example:

class Notifications
  def as_json
    # Fetch latest notifications via HTTP API
  end
end

class NotificationsController
  def index
    render json: { data: notifications.as_json }
  end

  def notifications
    Notifications.new
  end
end
Enter fullscreen mode Exit fullscreen mode

To avoid requesting remote API in tests, we use a double:

describe "NotificationsController#index" do
  let(:notifications) do
    double(:datasource, as_json: { notifications: [] })
  end

  before do
    allow(Notifications)
      .to receive(:new)
      .and_return(notifications)
  end

  it "wraps notifications in 'data' key" do
    get :index, format: :json

    expect(json_response["data"])
      .to have_key "notifications"
  end
end
Enter fullscreen mode Exit fullscreen mode

What happens if we rename Notifications#as_json to Notifications#to_json? Nothing. We will be left alone with a green test checking a useless double.

instance_double

To avoid getting into a situation described above, use instance_double:

describe 'NotificationsController#index' do
  let(:notifications) do
    instance_double(Datasource, as_json: { notifications: [] })
  end

  # ...
end
Enter fullscreen mode Exit fullscreen mode

Such a double will test its interface. If there is no method as_json or it has other arguments, it will fail the test.

To check mocks and stubs in the same way, make sure that verify_partial_doubles is enabled in spec_helper.rb.


More on this topic:

Discussion (0)

pic
Editor guide