Lets say you have a method on your User model called
User.find_activated, and you have models specs for it. Then in your
controller specs you mock it up like so:
@user = mock_model(User)
User.stub!(:find_activiated).and_return(@user)
Everything works great, and specs execute fast due to limited DB access.
What if I then refactor, and decide to rename the find_activated method
to a more descriptive User.find_all_activated ? So I change the model
spec to reflect the new name, then change the method name in the model,
and all specs pass. Awesome.
So I hit my app and it crashes. I get "no method find_activated for
User". My controller specs are mocking a method that no longer exists
in the real app. As a result, my specs pass, but my app crashes.
This would not have happened without the mocks, as the spec would have
failed since it called a non-existent method.
So, while I get the benefit of mocking (speed and isolation), it seems
like a gateway for passing specs and failing production applications.
Am I wrong? Am I going about mocking wrong?
--
Posted via http://www.ruby-forum.com/.
I've been looking at what it would take to establish a semi-automatic
mapping between specs and mocks. E.g. you could flag specs as
presenting a mock so that a change in the lower level object (and
attendant spec) would change the upstream mock behavior. In theory
this would help the sort of refactoring problems you're describing,
although there would still be sharp edges.
-faisal
Also there is an experimental feature in Mocha called responds_like or
quacks_like (http://mocha.rubyforge.org/classes/Mocha/Mock.html#M000029)
which constrains a mock to only allow methods that exist on a
specified object or class.
However, in the end there's no substitute for acceptance tests that
exercise critical business functionality.
--
James.
http://blog.floehopper.org
May I recommend that the default behaviour is that these are ignored,
but that you can explicitly tell mocha to fail or warn you of
expectations for methods that don't exist?
> Also there is an experimental feature in Mocha called responds_like or
> quacks_like (http://mocha.rubyforge.org/classes/Mocha/Mock.html#M000029)
> which constrains a mock to only allow methods that exist on a
> specified object or class.
>
> However, in the end there's no substitute for acceptance tests that
> exercise critical business functionality.
Hear! Hear!
It's important to understand that mocking as we approach it today
comes from TDD as part of an XP process, which divides tests into
Customer Tests and Programmer Tests (note: the lingo has morphed over
time, but the distinction has not). The idea is that you begin with
customer defined end-to-end tests (that fail miserably at first) and
use those to steer you in the right direction in terms of what objects
to develop. Then you drive the development of those objects with more
focused tests.
In this environment, mocking allows us to keep the programmer tests
focused on small bits of functionality. This makes it much easier to:
- develop code when the other pieces it relies on don't exist yet
- understand failures
- run the tests fast
The cost is the scenario Alex described: programmer tests all pass,
but a customer test fails. This is a GOOD THING. This is why we have
different levels of testing. If every level of testing exercises
everything in its entire environment, then we really only have one
level of testing, and we lose the unique benefits we intent to reap
from having different levels of testing.
Conversely, if you are not doing any high level testing in addition to
the object-level testing, then you probably shouldn't be using mocks
at all.
Cheers,
David
http://blog.davidchelimsky.net
> --
> James.
> http://blog.floehopper.org
>
> >
>