[mocha-developer] Organizing tests and mocha expectations

Bryan Helmkamp bhelmkamp at gmail.com
Tue Oct 10 09:54:55 EDT 2006


On 10/10/06, David Chelimsky <dchelimsky at gmail.com> wrote:
> The second suggestion you had makes the binding between the stub and
> the expectation clear to the reader and leaves you the flexibility to
> return the same values or different values:
>
> # suggestion 2
> object.stubs(:expected_method).returns(:result)
> object.expects(:expected_method, :replace => true).with(:parameter1,
> :parameter2).returns(:result)

I tend to agree with your reasoning, David.  Suggestion #3 does not
feel like the best option to me.  You're right that, in this case,
repeating the result of the method call is probably more clear than a
completely DRY solution.

> Bryan - in your original example you stated that following rspec's
> methodology makes you want to have the spec broken into three separate
> specs. I don't think that's really necessary in this case. In fact,
> binding specs together in any way is the antithesis of rspec's
> methodology. Every spec should run completely independently from the
> others and should never depend on state left behind by another spec.

I see where you are coming from here.  Obviously, removing one spec
should never cause another spec to fail.  On the other hand, I don't
think that what I was considering would necessarily introduce a
dependency between specs.  The only dependency would be between the
specs and the context, which I think is okay.

> The spec name is "update client invalid data should render edit form",
> so (if I understand correctly - maybe I don't) the fact that the
> client data is invalid is not really the issue. The goal is that when
> AR raises RecordInvalid you should render the edit form. The spec you
> suggested has some information in it that is not relevant to the
> meaning of the spec - specifically the stuff about attributes. To that
> end, a single spec w/ less information would be just fine:

Yes, you're spot on.  This is probably heading towards off-topic, but
here's the best I could come up with (that still passes):

context "update client" do
  include ClientsControllerSpecHelper

  specify "should use client param" do
    setup_controller

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

    @client.stubs(:save!)
    @controller.stubs(:render)

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

context "update client valid data" do
  include ClientsControllerSpecHelper

  def setup
    setup_controller

    @client = stub_everything
    Client.expects(:find).with("1").returns(@client)
    @client.expects(:save!).returns(true)

    put :update, :id => 1
  end

  specify "should redirect" do
    assert_response :redirect
  end
end

context "update client invalid data" do
  include ClientsControllerSpecHelper

  specify "should render edit form" do
    setup_controller

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

    @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
  end
end

I was able to remove the references to the attributes= method in the
valid and invalid data specs by using stubs_everything.  I'm not very
fond of having to repeat the setup for the Client#find call
everywhere.

Also, it's a bit disheartening to have to resort to 43 lines of specs
to test three relatively simple aspects of a 7 line controller method.
 Are there any ways to reduce the volume of this code that I might be
missing?  or is this 6:1 code to test ratio a necessary consequence of
the system?

Thanks for the help, David.

-Bryan


More information about the mocha-developer mailing list