Let me put my dilemma as an example. Take a look at a snippet from
FooTest.
#using mocha
def test_method1
Bar.expects(:method2).with(‘param1’, ‘param2’).once
Foo.method1
end
And now the implementation
class Foo
def self.method1
Bar.method2(‘param1’, ‘param2’)
end
end
class Bar
end
So far so good… Now my dilemma is that while implementing method2 in
Bar, I can come up with any weird signature (e.g. def method2
someInteger, someOtherNumber) and I will not be breaking FooTest. This
obviously makes life hard while refactoring. Coming from a Java/C#
background, I used to rely on compilation to catch these issues.
How have people solved such problems?
Pradeep
On 13/11/2007, Pradeep G. [email protected] wrote:
And now the implementation
So far so good… Now my dilemma is that while implementing method2 in
Bar, I can come up with any weird signature (e.g. def method2
someInteger, someOtherNumber) and I will not be breaking FooTest. This
obviously makes life hard while refactoring. Coming from a Java/C#
background, I used to rely on compilation to catch these issues.
How have people solved such problems?
Pradeep
Hi Pradeep,
Fundamentally you should never rely on only mock based tests, you should
always have some functional tests to check all the objects are wired
together correctly, but…
Mocha [1] used to only allow you to mock existing methods on
concrete
classes. This functionality accidentally got removed a while ago, but
I’d
like to reintroduce it asap.
You can already restrict expecations on mocks by using the
responds_like
modifier [2], something like this…
class Foo
def bar
end
end
def test_me
foo = mock(‘foo’)
foo.responds_like(Foo.new)
foo.expects(:not_bar)
foo.not_bar
end
=> NoMethodError: undefined method `not_bar’ for #Mock:foo which
responds like #Foo:0x432e5c
–
James.
[1] http://mocha.rubyforge.org
[2] http://mocha.rubyforge.org/classes/Mocha/Mock.html#M000032
Hi James,
Sorry for vanishing for a couple of weeks. I have been using ur
suggested approach of ‘responds_like’. Although its a good start, it
still does not give me complete confidence. All it tells me is whether
the method is present or not. It does not guarantee signature
compliance. For e.g., referring back to my example…
class Bar
def method2 param1
end
end
The above implementation of method2 in Bar will cause FooTest to pass
even with responds_like. But the 2 parameter signature is clearly not
supported.
I agree, an integration test should and will catch this scenario and
will fail. But its not possible to write integration tests for every
scenario. After all, the expected method call could happen under a
convoluted condition. A classic case where, I believe, the unit tests
should be able to give me complete guarantee of the state of code.
So there does appear to be a need for stricter checking. Leaving the
default behaviour as is, maybe introduce a new method like
mocked_object.strictly_expects(:blah)…
That leads me to another aspect? How about validating the return types
:D. Java/C# again gave me that additional safety net of knowing that the
return types were correct while I was mocking (at least thats what I
remember).
Pradeep
On 26/11/2007, Pradeep G. [email protected] wrote:
The above implementation of method2 in Bar will cause FooTest to pass
even with responds_like. But the 2 parameter signature is clearly not
supported.
I agree, an integration test should and will catch this scenario and
will fail. But its not possible to write integration tests for every
scenario. After all, the expected method call could happen under a
convoluted condition. A classic case where, I believe, the unit tests
should be able to give me complete guarantee of the state of code.
I don’t think you have to write an integration test for every
scenario. I would aim to write just enough to give decent coverage of
the integration points between classes, probably focussing on common
or important business scenarios.
So there does appear to be a need for stricter checking. Leaving the
default behaviour as is, maybe introduce a new method like
mocked_object.strictly_expects(:blah)…
I have considered something along these lines. I’ll give it some more
thought. I’ve added a feature request on rubyforge [1].
That leads me to another aspect? How about validating the return types
:D. Java/C# again gave me that additional safety net of knowing that the
return types were correct while I was mocking (at least thats what I
remember).
Hmm. I’m not sure how you are suggesting this could be achieved. Ruby
methods do not declare their return type in the method declaration. I
think the best way of obtaining the safety net you describe is to use
integration tests. Alternatively you could go back to a statically
typed language
–
James.
[1]
http://rubyforge.org/tracker/index.php?func=detail&aid=16769&group_id=1917&atid=7480
On Nov 13, 2007 7:18 AM, James M. [email protected] wrote:
Mocha [1] used to only allow you to mock existing methods on concrete
classes. This functionality accidentally got removed a while ago, but I’d
like to reintroduce it asap.
I beg of you, please don’t. At least not as a default behaviour.
Mocks are very powerful tools for interface discovery
(http://www.jmock.org/oopsla2004.pdf). With an enforcement rule like
the one you propose reinstating, we’d have to stop working on the
object at hand to go write a class and/or method. This would break the
flow of the current task, force us to shift focus.
Not only do we break the current flow, but by going over to the other
object and sticking in a stub to get the mock to shut up, we run a far
greater risk of leaving things 1/2 done than we do by sending
unsupported messages and have our integration tests expose those
holes.
For anybody who is serious about doing TDD, this would be a major step
backwards.
What we’ve talked about adding to … ahem … another mocking
library, is the ability engage this behaviour explicitly with an
environment variable or a command line switch. That would provide the
best of both worlds because you could stay focused on the task at hand
AND you could get a report of the methods you don’t have on
collaborating classes so you know where to go next.
I’d strongly recommend that you consider a similar path before simply
forcing this rule on mocha users.
Cheers,
David