I'm using Rspec to make some tests for my program. In a spec I instantiate the class once, and perform my tests on it using describes and contexts. I encountered something interesting, if the its seem to be evaluated at the end of the contexts. For example given the following classes and its associated spec:
class Tmp
def initialize
@values = {}
end
def modify(new_value1, new_value2)
@values = {:a => new_value1, :b => new_value2}
end
def get_values
return @values
end
end
describe Tmp do
tmp = Tmp.new()
describe "testing something" do
context "change value" do
# First evaluation
tmp.modify("hi", "bye")
it {tmp.get_values.should == {:a => "hi", :b => "bye"}}
# Second evaluation
tmp.modify("bye", "hi")
it {tmp.get_values.should == {:a => "bye", :b => "hi"}}
end
end
end
Using the provided class and spec the results are as follows:
F.
Failures:
1) Tmp testing something change value
Failure/Error: it {tmp.get_values.should == {:a => "hi", :b => "bye"}}
expected: {:a=>"hi", :b=>"bye"}
got: {:a=>"bye", :b=>"hi"} (using ==)
Diff:
@@ -1,3 +1,3 @@
-:a => "hi",
-:b => "bye"
+:a => "bye",
+:b => "hi"
# ./spec/tmp_spec.rb:11:in `block (4 levels) in <top (required)>'
Finished in 0.00211 seconds
2 examples, 1 failure
This is interesting as Rspec seems to evaluate the first it with the values from tmp as it is at the end of the context. Even though within the context the tmp is changing its values and should pass, Rspec evaluates the its based on the last values that variables have at the ending (I even tried this with a local primitive variable within the context and has a similar experience).
Is there a way to get around this and have the its evaluated in order? Or to at least get the following test to pass? I know I can use different variables and it will work, but there must be a way around this. I also want to know if this is an intended effect for Rspec.
UPDATE with respect to Philipe's answer
By making the change within a single it block the spec passes:
describe Tmp do
describe "do something" do
let(:instance) {Tmp.new}
it 'should be modifiable' do
instance.modify('hi', 'bye')
instance.values.should == {a: 'hi', b: 'bye'}
instance.modify('bye', 'hi')
instance.values.should == {a: 'bye', b: 'hi'}
end
end
end
But if I use the subject it seems to revert back and fails on the first should
describe Tmp do
describe "do something" do
let(:instance) {Tmp.new}
subject{instance.values}
it 'should be modifiable' do
instance.modify('hi', 'bye')
should == {a: 'hi', b: 'bye'}
instance.modify('bye', 'hi')
should == {a: 'bye', b: 'hi'}
end
end
end
Not sure why this is the case. At least I see that the changes should be within a it block to better reflect the changes we're testing for.