Rspec Class any_instance expects method_name - where it is documented? - ruby

I've stumbled upon the following piece of code in an Rspec test and I must say I more or less figured out what it does but I can't find relevant sources to prove it. Please point me to a gem or docs that describe:
describe SomeModule::Salesforce::Lead do
before do
SomeModule::Salesforce::Lead.any_instance.expects(:materialize)
end
...
end
It seems that for :each example in this spec it sets expectation on any instance of the class described above to receive a call to :materialize method AND it actually redefines the method to do nothing . The last part seems crucial because it avoids connecting to SalesForce in test environment but I can't find confirmation for this.

any_instance is documented under Working with Legacy code
You are correct in that it both sets an expectation and stubs out the original method on any given instance of a class.
Previous versions of RSpec accomplish this by monkeypatch the ruby core classes (Object and BaseObject)
RSpec 3 has a new syntax which does not rely on monkeypatching:
before do
expect_any_instance_of(SomeModule::Salesforce).to receive(:materialize)
end

Ok I've just found that I was looking in wrong sources, it doesn't come from RSpec but from Mocha Mock (expects and any_instance) http://gofreerange.com/mocha/docs/Mocha/Mock.html#expects-instance_method
Thanks #tomasz-pajor #https://stackoverflow.com/users/2928259/tomasz-pajor

Related

RSpec stubs apparently not cleaned up after test

I've been working to diagnose a test failure that only occurs on my master branch. Following is the relevant code, in simplified form.
There's a service:
class Service
attr_reader :traces
def initialize
#traces = []
end
def do_work
#traces << Thread.current.backtrace
# ... actual work ...
end
end
And a class that makes use of the service:
class Widget
def get_cached_service
puts("Getting a cached service!")
puts("Do I already have one? #{!!#service}")
#service ||= make_service
end
def make_service
puts("Making a service!")
Service.new
end
end
I have a test (that lives in a file widget_spec.rb) that fails intermittently. This test creates an instance of Widget and calls get_cached_service. I see the Getting a cached service! message on the console, followed by Do I already have one? false, but I don't see the Making a service! message.
Furthermore, when I examine the traces attribute of the returned Service object, I find stack traces originating from other tests in my project (eg. foo_spec.rb, bar_spec.rb, etc).
In a few different places I find code like:
allow_any_instance_of(Widget)
.to receive(:make_service).and_return(whatever)
The other tests whose stack traces I find are likely stubbing make_service like this. But it appears that the stubbing is not being undone after those tests, as should always happen according to my understanding.
Is there any reason, other than a bug in rspec, that could cause a stub not to be reset at the end of a test?
The stub is almost certainly being cleared, but you’ve cached the fake instance in get_cached_service. Nothing clears the cached value in #service, and RSpec (rightfully) doesn’t know about it. As such, stubbing make_service is not enough if tests call get_cached_service. You have a few options:
Always stub get_cached_service instead of, or in addition to, make_service
Provide a way to clear the cached value which is called after each test.
Make the caching configurable in some way, or a wrapper around the actual implementation, such that the caching does not occur in test code.
I realise this is quite late to answer, but for posterity for anyone who reads this:
Use rspec bisect to figure out if there is a consistent test ordering that causes failure, then start ripping code out until you're left with only the bit that breaks.
I can't remember a case where RSpec is at fault - almost invariably, somewhere there is a class variable that isn't getting cleared, or someone is manually playing with a class with something like define_method. Occasionally it might be happening in a gem.
Make sure everything is cleared after every test in your spec_helper - clear the Rails cache, clear ActionMailer deliveries, return from Timecop freezes, etc.
Anything directly RSpec-related should clear itself in theory, because it's designed to integrate into RSpec, and is probably the least likely explanation in general.

Minitest stub passing block to mock instance

I stumbled across a curious behaviour and haven't been able to figure out what I was doing wrong. I hope somebody can enlighten me.
I was trying to stub the Redis client during my tests in a Rails application. Therefore I was using the MockRedis gem. I have created a RedisFactory class with the single class method .create, which I wanted to stub with an optional MockRedis instance like so:
def stub_redis(mock_redis = MockRedis.new)
RedisFactory.stub :create, mock_redis { yield }
end
This did not work and always threw a ArgumentError in Foo#Bar wrong number of arguments (0 for 1). Some further debugging revealed that a call of RedisFactory.create 'foo' within the stub-block resulted in an error that 'foo' is no method on instance of MockRedis::Database.
However, I have been able to solve this problem with the following code snippet, using a lambda function to catch the incoming arguments:
def stub_redis(mock_redis = MockRedis.new)
RedisFactory.stub(:create, ->(*_args) { mock_redis }) { yield }
end
Could anybody explain this behaviour?
As of now MiniTest tries to guess if the passed val_or_callable is a Proc by checking whether it responds to call, cf.:
https://apidock.com/ruby/Proc/call
https://github.com/seattlerb/minitest/blob/b84b8176930bacb4d70d6bef476b1ea0f7c94977/lib/minitest/mock.rb#L226
Unfortunately, in this specific case Redis as well as the passed MockRedis-instance both provide a generic call-method for executing Redis commands, cf.:
https://github.com/brigade/mock_redis/blob/master/lib/mock_redis.rb#L51
You already found the correct workaround. In this case, your only chance is to explicitly use the proc-version of stub.
Note: There are some communities using def call as a pattern with ServiceObjects in Ruby which may have a difficult time using minitest's stub. It is probably a good idea to open an issue in seattlerb/minitest.

define method inside block

I'm new to ruby, I'm studying ruby by writing unittest with RSpec, and there is one line of code I can't understand in RSpec when define custom matcher.
RSpec::Matchers.define :be_a_multiple_of do |expected|
match do |actual|
do_the_math(actual, expected)
end
def do_the_math(actual, expected)
actual % expected == 0
end
end
as far as I know, ruby is a script language that the compiler read the code sequencly. that is said you must define the method before you use it. but in RSpec, when I define a custom matcher, I could define the helper method after I call it.
I have write the test code to test the fact that it will fail if I call the method before I define it and I also read a little source code from RSpec the define method actually is a wrapper of define_method and pass the delegate as block. but I still can't understand how this code work.
can somebody help me out? just a brief explaination of how this work
Short explanation is: you are not calling this method yet in there, you are only saying to call it when you call be_a_multiple_of matcher, and the method is already defined when that happens.
Parser is not checking whether methods are defined or not when declaring a method or block - as the method might be defined later or there might be a missing_method fall_back.

How do I stub out a specific file with rspec?

I've searched far and wide and I hope someone can answer this question. I'm using the following code to stub out the 'exists?' method for FileTest in an rspec spec:
it "returns no error if file does exist" do
#loader = MovieLoader.new
lambda {
FileTest.stub!(:exists?).and_return(true)
#loader.load_file
}.should_not raise_error("File Does Not Exist")
end
What I really want to do is to ensure that the existence of a very specific file is stubbed out. I was hoping something like this would do the job:
it "returns no error if file does exist" do
#loader = MovieLoader.new
lambda {
FileTest.stub!(:exists?).with(MovieLoader.data_file).and_return(true)
#loader.load_file
}.should_not raise_error("File Does Not Exist")
end
However, this doesn't seem to be working. I am having a very difficult time finding documentation on what the 'with' method actually does. Perhaps I'm barking up the wrong tree entirely.
Can someone please provide some guidance?
The RSpec stubbing framework leaves a bit to be desired, and this is one of those things. The stub!(:something).with("a thing") ensures that each time the something method is called that it receives "a thing" as the input. If it receives something other than "a thing", RSpec will stop the test and report an error.
I think you can achieve what you want, you'll just have to approach this a little differently. Instead of stubbing out FileTest, you should be stubbing out a method on your #loader instance, and that method would normally call FileTest.exists?. Hopefully this demonstrates what I'm getting at:
class MovieLoader
def load_file
perform_loading if file_exists?(file_path)
end
def file_exists?(path)
FileTest.exists? path
end
end
Your test would then look like:
it "returns no error if file does exist" do
#loader = MovieLoader.new
lambda {
#loader.stub!(:file_exists?).with(MovieLoader.data_file).and_return(true)
#loader.load_file
}.should_not raise_error("File Does Not Exist")
end
Now you are only stubbing one instance of the loader, so other instances will not inherit the stubbed version of file_exists?. If you need to be more fine-grained than that, you'll probably need to use a different stubbing framework, which RSpec supports (stubba, mocha, etc).

Ruby Testing: Testing for the Absence of a Method

In rspec and similar testing frameworks, how does one test for the absence of a method?
I've just started fiddling with rspec and bacon (a simplified version of rspec.) I wanted to define test that would confirm that a class only allows read access to an instance variable. So I want a class that looks like:
class Alpha
attr_reader :readOnly
#... some methods
end
I am rather stumped:
it "will provide read-only access to the readOnly variable" do
# now what???
end
I don't see how the various types of provided test can test for the absence of the accessor method. I'm a noob in ruby and ruby testing so I'm probably missing something simple.
In Ruby, you can check if an object responds to a method with obj.respond_to?(:method_name), so with rspec, you can use:
Alpha.new.should_not respond_to(:readOnly=)
Alternatively, since classes could override the respond_to? method, you can be stricter and make sure that there is no assignment method by actually calling it and asserting that it raises:
expect { Alpha.new.readOnly = 'foo' }.to raise_error(NoMethodError)
See RSpec Expectations for reference.
I believe you're looking for respond_to?, as in some_object.respond_to? :some_method.

Resources