Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stubbing instance variables in RSpec 3.2

I'm using RSpec 3.2 in Rails and wondering how I can stub instance variables if they are not publicly made accessible by a getter method (e.g. using attr_accessor or similar).

Consider the simple example below -

require 'rails_helper'

class A
  def initialize
    @x = 3
  end

  def add(n)
    sum = @x + n
    puts "Sum is #{sum}"
    sum
  end
end


RSpec.describe A do
  before(:all) do
    @a = A.new
  end

  it 'computes the sum correctly' do
    # For this test I want to stub the value of @x and return 5
    allow(@a).to receive(:x) { 5 }
    # 5 + 8 should return 13
    expect(@a.add(8)).to eq(13)
  end
end

Trying to stub @x is not possible in this scenario as the class never never receives a message or method call for x. The RSpec output confirms this -

Failures:

  1) A computes the sum correctly
     Failure/Error: allow(@a).to receive(:@x) { 5 }
       #<A:0x007fe6e66ab8d0 @x=3> does not implement: @x
     # ./spec/unit/test_spec.rb:25:in `block (2 levels) in <top (required)>'

I could work around this by making the instance variable @x accessible (attr_accesssor :x) and replacing the call to @x with self.x, but that seems hacky and may not be possible in my more complex implementation.

Is there a better way to stub this?

Thanks!

like image 344
user2490003 Avatar asked Dec 09 '22 04:12

user2490003


1 Answers

I think this is not a correct practice. Rspec should be testing the interface behaviour of classes, not the internal implementation.

As you are not using accessor, you can use #instance_variable_set & #instance_variable_get to manipulate and get the instance variable.

Getting and Setting are like follows:

@a.instance_variable_set(:@x, 5)
@a.instance_variable_get(:@x)
#=> 5

In your code:

@a.instance_variable_set(:@x, 5)
expect(@a.add(8)).to eq(13)
like image 188
Sharvy Ahmed Avatar answered Dec 22 '22 07:12

Sharvy Ahmed