[mocha-developer] Organizing tests and mocha expectations

James Mead jamesmead44 at gmail.com
Mon Oct 9 04:45:37 EDT 2006


On 09/10/06, Bryan Helmkamp <bhelmkamp at gmail.com> wrote:
>
> I'm simultaneously getting into using Mocha and RSpec-style tests
> (courtesy of the simply_bdd plugin) and I'm struggling with some
> issues while trying to organize my specs/test.  Here's a code example
> illustrating the problem:
>
> context "update cliient invalid data" do
>   include ClientsControllerSpecHelper
>
>   specify "should render edit form" do
>     setup_controller
>
>     @client = mock
>     Client.expects(:find).with("1").returns(@client)
>
>     @client.expects(:attributes=).with("attributes")
>     @client.expects(:save!).raises(ActiveRecord::RecordInvalid.new
> (stub(:errors
> => stub(:full_messages => []))))
>
>     @controller.expects(:render).with()
>     @controller.expects(:render).with(:action => "edit")\
>
>     put :update, :id => 1, :client => "attributes"
>   end
> end
>
> Now, to fit well with RSpec's methodology, I'd like to have a setup
> method which gets the context ready, and then follow that up with some
> specifications.  Essentially here's the logic:
>
> Update client with invalid data
> - Should find client by id
> - Should attempt to save client with new attributes
> - Should render edit form
>
> The problem is that I can't break the expectations up into separate
> test methods like that because all the tests depend on those in order
> to work.  It's as if each of those expectations is both setup code and
> a test, and each test relies on all the setup code for the whole test
> case.
>
> Is  there anyway to reorganize this code to still be DRY but yet also
> achieve more granular tests, despite their interdependencies?
>
> Thanks,
>
> -Bryan


If I understand correctly, I think your problem boils down to the fact that
the mock is providing two pieces of functionality for each expectation - (1)
asserts that a particular method has been called with particular parameters;
(2) returns a specified result (stubbing). I'm not overly familiar with the
BDD style, but the only way I can see you could do this at the moment is...

context "update cliient invalid data" do
  include ClientsControllerSpecHelper

  specify "should find client by id" do
    setup_controller

    @client = mock
    Client.expects(:find).with("1").returns(@client)

    @client.stubs(:attributes=)
    @client.stubs(:save!).raises(ActiveRecord::RecordInvalid.new(stub(:errors
=> stub(:full_messages => []))))

    @controller.stubs(:render)

    put :update, :id => 1, :client => "attributes"
  end

  specify "should attempt to save client with new attributes" do
    setup_controller

    @client = mock
    Client.stubs(:find).returns(@client)

    @client.expects(:attributes=).with("attributes")
    @client.expects(:save!).raises(ActiveRecord::RecordInvalid.new(stub(:errors
=> stub(:full_messages => []))))

    @controller.stubs(:render)

    put :update, :id => 1, :client => "attributes"
  end

  specify "should render edit form" do
    setup_controller

    @client = mock
    Client.stubs(:find).returns(@client)

    @client.stubs(:attributes=)
    @client.stubs(:save!).raises(ActiveRecord::RecordInvalid.new(stub(:errors
=> stub(:full_messages => []))))

    @controller.expects(:render).with()
    @controller.expects(:render).with(:action => "edit")

    put :update, :id => 1, :client => "attributes"
  end

end

...admittedly not very DRY, but I don't think DRY-ness is as important as
readability in tests.

However this reminds me of a conversation I had a while ago with my
colleague Chris Roos. We were discussing the possibility of replacing one
expectation with another. If we could do this then it might be possible to
do something like this...

context "update cliient invalid data" do
  include ClientsControllerSpecHelper

  setup do
    setup_controller

    @client = mock
    Client.stubs(:find).returns(@client)

    @client.stubs(:attributes=)
    @client.stubs(:save!).raises(ActiveRecord::RecordInvalid.new(stub(:errors
=> stub(:full_messages => []))))

    @controller.stubs(:render)
  end

  specify "should find client by id" do
    Client.expects(:find).with("1").returns(@client)

    put :update, :id => 1, :client => "attributes"
  end

  specify "should attempt to save client with new attributes" do
    @client.expects(:attributes=).with("attributes")
    @client.expects(:save!).raises(ActiveRecord::RecordInvalid.new(stub(:errors
=> stub(:full_messages => []))))

    put :update, :id => 1, :client => "attributes"
  end

  specify "should render edit form" do
    @controller.expects(:render).with()
    @controller.expects(:render).with(:action => "edit")

    put :update, :id => 1, :client => "attributes"
  end

end


...I'm guessing this is more like what you're after. If yes, let me know and
we'll look into making the change.

-- 
James.
http://blog.floehopper.org
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://rubyforge.org/pipermail/mocha-developer/attachments/20061009/655df30c/attachment.html 


More information about the mocha-developer mailing list