[rspec-users] When to use Factories, Mock Models, Mocks & Stubs

David Chelimsky dchelimsky at gmail.com
Wed Feb 3 10:44:45 EST 2010


On Wed, Feb 3, 2010 at 8:52 AM,  <me at franklakatos.com> wrote:
> Ok, so these ideas seem kind of natural to me, which is nice:
>
> mock_models being used to mock non-tested models
> stub for queries and/or well-tested methods, should_receives for commands
>
> While reading over Dave Astlels, I kind of got concerned because of
> something he states that I feel I'm doing in my specs:
>
> "When you realize that it's all about specifying behaviour and not writing
> tests, your point of view shifts. Suddenly the idea of having a Test class
> for each of your production classes is ridiculously limiting. And the
> thought of testing each of your methods with its own test method (in a
> 1-1 relationship) will be laughable."
>
> This is what I am striving for, but being guided simply by rSpec error
> messages results me in writing specs like this...
>
> describe "POST 'create'" do
>
>    before do
>      @current_user = mock_model(User)
>      controller.stub(:current_user).and_return @current_user
>      @company = mock_model(Company)
>      @current_user.should_receive(:company).and_return @company
>      @clients = mock("Client List")
>      @company.should_receive(:clients).and_return @clients
>    end
>
>    describe "when client is found" do
>
>      before do
>        @client = mock_model(Client)
>        @clients.should_receive(:find).and_return @client
>      end
>
>      describe "on successful save" do
>
>        before do
>          @projects = mock_model(ActiveRecord)

This is a little odd. @projects is a collection, not a an instance,
and mocking ActiveRecord explicitly seems a bit odd. I'd generally us
a simple array:

@projects = []

>          @client.should_receive(:projects).and_return @projects
>          @project = mock_model(Project)
>          @projects.should_receive(:build).and_return @project
>          @client.should_receive(:save).and_return true
>          @project.should_receive(:name).and_return "New Project"
>        end
>
>        it "should set up the flash" do
>          post "create", {:project => {:client_id => 1}}
>          flash[:notice].should_not be_nil
>        end
>
>      end
>
>    end
>
>
>  end
>
>
>
> ... for a controller that looks like this ...
>
>
> def create
>      @client =
> current_user.company.clients.find(params[:project][:client_id])
>      @project = @client.projects.build(params[:project])
>      if @client.save
>        flash[:notice] = "Added: #{@project.name}"
>      else
>        render :new
>      end
>  end
>
>
>
> Am I doing the 1-1 thing that BDD specifically set out to avoid?

1-1 example per method is probably a red flag, but 1-1 spec file per
implementation file makes navigation easier, so I think it's actually
a good thing.

The underlying problem with 1-1 mappings stems from IDE's that will
make an empty test case by reflecting on an untested object. You'd end
up with 50 line long test methods named "testGetName" that actually
contain 20 different tests in the one method. That's an extreme, but I
used to see that sort of thing all the time when I was consulting, and
it makes it very difficult to understand what is being tested and what
went wrong when there is a failure.

Make sense?

- David
>
>
>
>
>
>
>
>
>
> Quoting Adam Sroka <adam.sroka at gmail.com>:
>
>> On Tue, Feb 2, 2010 at 9:53 PM, Andrei Erdoss <erdoss at gmail.com> wrote:
>>>
>>> Hello Frank,
>>>
>>> From my understanding these are the roles of should_receive and stub.
>>>
>>> should_receive checks to make sure that a method or a property is called.
>>> To
>>> this you can specify the arguments that it gets called (.with()), what it
>>> returns (.and_return) and how many times this happens (.once, .twice
>>> etc).
>>>
>>> stub on the other hand is a place holder for functions calls that have
>>> been
>>> tested already or are Rails defaults, which don't need to be tested.
>>> stubs
>>> are used in conjunction with mock_models, in order to provide for the
>>> functions or properties that are needed for the code to run, up to the
>>> test
>>> point.
>>>
>>
>> I think that it is best to think of these in terms of command query
>> separation. In case you aren't familiar with that principle, it states
>> that some methods are commands - they tell an object to do something
>> but don't return anything interesting, and other methods are queries -
>> they return some interesting value but have no side effects.
>>
>> should_receive is how we set an expectation for a command. We don't
>> really care what a command returns but we do care that it gets called.
>> should_receive literally says that the command should be called with
>> the given parameters.
>>
>> stub is how we handle a query. We care what a query returns, or rather
>> the code we are testing does, but we don't really care when it gets
>> called (or how often) per se. If we depend on its result then it
>> should be called, but the effect that the result has on the system
>> we're testing is what we really care about.
>> _______________________________________________
>> rspec-users mailing list
>> rspec-users at rubyforge.org
>> http://rubyforge.org/mailman/listinfo/rspec-users
>>
>
>
>
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>


More information about the rspec-users mailing list