[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

how to ensure signature compliance while mocking in ruby

Pradeep Gatram

11/13/2007 10:22:00 AM

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
--
Posted via http://www.ruby-....

4 Answers

James Mead

11/13/2007 1:18:00 PM

0

Note: parts of this message were removed by the gateway to make it a legal Usenet post.

On 13/11/2007, Pradeep Gatram <pradeep.gatram@gmail.com> wrote:
>
> 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


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.
http://blog.floe...
http://tumble.floe...

[1] http://mocha.rub...
[2] http://mocha.rub.../classes/Mocha/Mock.html#M000032

David Chelimsky

11/13/2007 2:44:00 PM

0

On Nov 13, 2007 7:18 AM, James Mead <jamesmead44@gmail.com> 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/oops...). 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

Pradeep Gatram

11/26/2007 12:54:00 PM

0

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
--
Posted via http://www.ruby-....

James Mead

1/2/2008 4:03:00 PM

0

On 26/11/2007, Pradeep Gatram <pradeep.gatram@gmail.com> wrote:
> 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.

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.
http://blog.floe...
http://tumble.floe...

[1] http://rubyforge.org/tracker/index.php?func=detail&aid=16769&group_id=1917&...