[rspec-users] Newbie: lambda do...end.should change(Model, :count).by(1). Doesn't work

Edvard Majakari edvard at majakari.net
Sat May 10 09:34:15 EDT 2008


>    def do_verb(params = @params)
>      post :create, :album => params
>    end
>    it "should change the Albums count by 1 after POST" do
>      lambda do
>        @album.should_receive(:save).and_return(true)
>        do_verb
>      end.should change(Album, :count).by(1)
>    end

You expect the database count to change, but because you mock away the
method :save,
the object won't get saved. You don't need to test for  Album#count to
increase; if the 'save' method is called
for the object, you can rest assured there's one more object in the
db. And if save doesn't work as it should, the problem
isn't in your code but in the ActiveRecord instead.

I'm not sure, but I guess that people who make this kind of mistake
think that mocking/stubbing a method doesn't
prevent the calling of the original method, but it does.

The whole idea in mocking is to specify _expectations related to
behaviour_. The following (simplified) rule could
be stated as follows: if you use mocks, you don't need to (nor should)
use state-based assertions, as
 lambda {...}.should change(Model, :method) does. And vice versa; if
you use state to test stuff, you don't use mocks. But as
I said, it is an oversimplification. For example, usually you don't
want to mock or stub methods to unit/thingy under the test, but
you _should_ probably mock/stub methods related to associated classes.

That said, some people consider use of mocks dangerous and brittle to
changes. I don't - if you have a language terse
enough (like Ruby) and an advanced speccing framework (like RSpec),
changing your tests to reflect intended changes to code
is not a chore too tedious for me. Besides, to ensure at least
mediocre test/spec coverage you should automate your integration tests
as well, where you don't use mock/stub objects at all (with the
exception of very expensive/non-deterministic resources such as say,
network and random number generators).

As for the other question, I learned BDD by reading the
well-documented RSpec site itself and random blog entries related to
BDD and Rails. But to really learn BDD well, I'd recommend any
newcomer to get acquaintanted with core concepts of BDD first, like
unit testing
and mocks/stubs. For the former I recommend books related to
Test-Driven Development like those by David Astel and Kent Beck, and
for the latter articles related to mocking: Fowler's Mocks Aren't
Stubs (http://martinfowler.com/articles/mocksArentStubs.html) is an
excellent explanation (or a viewpoint; some people don't make such
difference between the two) for both mocks and stubs, and the article
"Mock Roles, not Objects" (http://www.jmock.org/oopsla2004.pdf) is an
invaluable gem in itself -- for me it took two reads to really
understand (I hope!) it, though, with more than one year in between
(the catch is that the paper appears to be simple; it doesn't contain
cryptic formulas or ingenious mathematical proofs, however, I believe
it may take a while before the reader really, really understands the
subject of the article).

"One day, when he was naughty, Mr Bunnsy looked over the hedge into
Farmer Fred's field and it was full of fresh green lettuces. Mr
Bunnsy, however, was not full of lettuces. This did not seem fair."
 -- Terry Pratchett, Mr. Bunnsy Has An Adventure

More information about the rspec-users mailing list