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!
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With