Search code examples
ruby-on-railsrubyrspecinstance-variables

How to access an object's instance variables while stubbing?


I'm writing some tests for a web application and one of the controllers calls resolve on a Scope object, which returns a slightly modified scope. In the test I would like to stub this method to return the original scope (passed as a parameter to Scope.initialize).

The Scope object

class Scope

  def initialize(scope)
    @scope = scope
  end

  def resolve
    # Return a modified scope.
  end
end

The controller

class FooController < ApplicationController

  def show
    foos = Scope.new(Foo.some_foos).resolve
    respond_with foos
  end
end

The test

it "does something" do
  allow_any_instance_of(Scope).to receive(:resolve).and_return(???.scope)
  get :show
  # Do some assertions.
end

What do I need to put where the ??? are in order to stub the resolve method on any instance of Scope to return the original scope that it was created with?

I'm using Rspec 3.4.2.


Solution

  • First you will want to create a attribute reader on Scope so that you can access @scope without using instance_variable_get:

    class Scope
    
      attr_reader :scope
    
      def initialize(scope)
        @scope = scope
      end
    
      def resolve
        # Return a modified scope.
      end
    end
    

    If you use block implementation the receiver is passed as the first arg:

    allow_any_instance_of(Scope).to receive(:resolve) do |s|
      s.scope
    end
    

    However using allow_any_instance_of is strongly discouraged and usually a sign that your tests are poking around too much in how your controllers do their job instead of actually testing their behavior in a future proof way.

    I would instead use a unit test which tests Scope and request specs which tests the controller in conjunction with feature specs. This is how I test applications that use Pundit and is a robust strategy.