Open expeehaa opened 3 years ago
Looks reasonable. Would you like to hack on this?
Wondering how do you use nested spaces in your specs, can you give a hint?
I'll maybe try to write a fix, but it could take a while. If someone else wants to do it earlier I'ld be totally fine with it.
Regarding the usage: I have a class that is instantiated with some well-defined arguments and the initializer also conditionally calls a method (let's call it C.rnd_method
) that returns a random value that I unfortunately cannot pass to the method. My spec tests a method that uses this random value and returns a string with it. I don't want to interpolate the expected string, so I created a mock for the class initializer that mocks the random-value-returning method to return a specific value and calls the original initializer method.
Since C.rnd_method
is also used in other parts of the test code I wrapped the mock method code in RSpec::Mocks.with_temporary_scope
.
Something like this (very much simplified):
class A
def initialize(*args)
if some_condition
@test = C.rnd_method
end
...
end
end
RSpec.describe A do
before(:each) do
allow(A).to receive(:new).and_wrap_original do |method, *args|
RSpec::Mocks.with_temporary_scope do
allow(C).to receive(:rnd_method).once.and_return(5)
method.call(*args)
end
end
end
it 'works' do
expect(C).to receive(:rnd_method).twice.and_return(2)
some_method_that_calls_A_new_and_C_rnd_method
end
end
I suppose that, even though this is a simplified version, the real code could be optimized to not need the temporary scope.
I have some different problem with nested scopes. Maybe it is related…
The next code produces unexpected results for nested scopes:
class Keys
def Keys.private_key = '0'
end
RSpec.describe 'cases' do
it 'not nested mock' do
puts Keys.private_key
RSpec::Mocks.with_temporary_scope do
allow(Keys).to receive(:private_key).and_return('1')
puts ' '+Keys.private_key
end
puts Keys.private_key
RSpec::Mocks.with_temporary_scope do
allow(Keys).to receive(:private_key).and_return('2')
puts '' + Keys.private_key
end
puts Keys.private_key
RSpec::Mocks.with_temporary_scope do
allow(Keys).to receive(:private_key).and_return('3')
puts ' '+Keys.private_key
end
puts Keys.private_key
RSpec::Mocks.with_temporary_scope do
allow(Keys).to receive(:private_key).and_return('4')
puts ' '+Keys.private_key
end
puts Keys.private_key
end
it 'nested mock' do
puts Keys.private_key
RSpec::Mocks.with_temporary_scope do
allow(Keys).to receive(:private_key).and_return('1')
puts ' '+Keys.private_key
RSpec::Mocks.with_temporary_scope do
allow(Keys).to receive(:private_key).and_return('2')
puts ' '+Keys.private_key
RSpec::Mocks.with_temporary_scope do
allow(Keys).to receive(:private_key).and_return('3')
puts ' '+Keys.private_key
RSpec::Mocks.with_temporary_scope do
allow(Keys).to receive(:private_key).and_return('4')
puts ' '+Keys.private_key
end
puts ' '+Keys.private_key
end
puts ' '+Keys.private_key
end
puts ' '+Keys.private_key
end
puts Keys.private_key
end
end
Results:
not nested mocks:
0
1
0
2
0
3
0
4
0
nested mocks:
0
1
2
3
4
4
2
2
0
Subject of the issue
When mocking a method in an outer and inner scope using combinations of
expect
andallow
, the mocks leak out of the inner scope. I suppose that this is a bug because it only happens when the method was first mocked in the outer scope. If it is a feature it is a pretty unintuitive one.Your environment
Steps to reproduce
Expected behavior
All tests should pass.
Actual behavior