Reputation: 7135
My question is similar to this one: Mocha: stubbing method with specific parameter but not for other parameters
obj.expects(:do_something).with(:apples).never
perform_action_on_obj
The perform_action_on_obj
will not call do_something(:apples)
as I expect. However, it may call do_something(:bananas)
. If it does, I get an unexpected invocation failure.
My understanding is that since I placed never
at the end of the expectation, it only applied to that specific modified expectation. However it appears that once I start mocking behavior on obj
I've "screwed it up" in a sense.
How can I allow other invocations of the do_something
method on obj
?
EDIT: Here is a clear cut example that demonstrates my issue perfectly:
describe 'mocha' do
it 'drives me nuts' do
a = mock()
a.expects(:method_call).with(:apples)
a.lol(:apples)
a.lol(:bananas) # throws an unexpected invocation
end
end
Upvotes: 6
Views: 4938
Reputation: 2294
have you tried the block feature of with
?
obj.stubs(:do_something).with(:apples)
obj.stubs(:do_something).with { |p| p == :apples }
obj.stubs(:do_something).with { |p| p != :apples }
calls to do_something
with anything other than :apples
will pass, whereas obj.do_something(:apples)
will yield an unexpected invocation.
Upvotes: 3
Reputation: 1188
The accepted answer didn't work form me, so I figured out this work around that does in my case.
class NeverError < StandardError; end
obj.stubs(:do_something).with(:apples).raises(NeverError)
obj.do_something(:bananas)
begin
obj.do_something(:apples)
rescue NeverError
assert false, "expected not to receive do_something with apples"
end
There's room for improvement, but this gives the gist and should be easy to modify to fit most scenarios.
Upvotes: 1
Reputation: 1709
Here's a workaround using ParameterMatchers:
require 'test/unit'
require 'mocha/setup'
class MyTest < Test::Unit::TestCase
def test_something
my_mock = mock()
my_mock.expects(:blah).with(:apple).never
my_mock.expects(:blah).with(Not equals :apple).at_least(0)
my_mock.blah(:pear)
my_mock.blah(:apple)
end
end
Result:
>> ruby mocha_test.rb
Run options:
# Running tests:
F
Finished tests in 0.000799s, 1251.6240 tests/s, 0.0000 assertions/s.
1) Failure:
test_something(MyTest) [mocha_test.rb:10]:
unexpected invocation: #<Mock:0xca0e68>.blah(:apple)
unsatisfied expectations:
- expected never, invoked once: #<Mock:0xca0e68>.blah(:apple)
satisfied expectations:
- allowed any number of times, invoked once: #<Mock:0xca0e68>.blah(Not(:apple))
1 tests, 0 assertions, 1 failures, 0 errors, 0 skips
In general, I agree with you: this behavior is frustrating to work with and violates the principle of least surprise. It's also hard to extend the trick above to more general cases, since you'll have to write an increasingly complicated 'catchall' expression. If you want something more intuitive, I find RSpec's mocks to be quite nice.
Upvotes: 10
Reputation: 695
This is more of a hack than a real answer explaining mocha's behavior but perhaps the following will work?
obj.expects(:do_something).with(:apples).times(0)
Mocha will set the cardinality instance variable using times rather than exactly.
Upvotes: 1