On 09/10/06, <b class="gmail_sendername">Bryan Helmkamp</b> &lt;<a href="mailto:bhelmkamp@gmail.com">bhelmkamp@gmail.com</a>&gt; wrote:<div><span class="gmail_quote"></span><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
I'm simultaneously getting into using Mocha and RSpec-style tests<br>(courtesy of the simply_bdd plugin) and I'm struggling with some<br>issues while trying to organize my specs/test.&nbsp;&nbsp;Here's a code example<br>illustrating the problem:
<br><br>context &quot;update cliient invalid data&quot; do<br>&nbsp;&nbsp;include ClientsControllerSpecHelper<br><br>&nbsp;&nbsp;specify &quot;should render edit form&quot; do<br>&nbsp;&nbsp;&nbsp;&nbsp;setup_controller<br><br>&nbsp;&nbsp;&nbsp;&nbsp;@client = mock<br>&nbsp;&nbsp;&nbsp;&nbsp;Client.expects
(:find).with(&quot;1&quot;).returns(@client)<br><br>&nbsp;&nbsp;&nbsp;&nbsp;@client.expects(:attributes=).with(&quot;attributes&quot;)<br>&nbsp;&nbsp;&nbsp;&nbsp;@client.expects(:save!).raises(ActiveRecord::RecordInvalid.new(stub(:errors<br>=&gt; stub(:full_messages =&gt; []))))
<br><br>&nbsp;&nbsp;&nbsp;&nbsp;@controller.expects(:render).with()<br>&nbsp;&nbsp;&nbsp;&nbsp;@controller.expects(:render).with(:action =&gt; &quot;edit&quot;)\<br><br>&nbsp;&nbsp;&nbsp;&nbsp;put :update, :id =&gt; 1, :client =&gt; &quot;attributes&quot;<br>&nbsp;&nbsp;end<br>end<br><br>Now, to fit well with RSpec's methodology, I'd like to have a setup
<br>method which gets the context ready, and then follow that up with some<br>specifications.&nbsp;&nbsp;Essentially here's the logic:<br><br>Update client with invalid data<br> - Should find client by id<br> - Should attempt to save client with new attributes
<br> - Should render edit form<br><br>The problem is that I can't break the expectations up into separate<br>test methods like that because all the tests depend on those in order<br>to work.&nbsp;&nbsp;It's as if each of those expectations is both setup code and
<br>a test, and each test relies on all the setup code for the whole test<br>case.<br><br>Is&nbsp;&nbsp;there anyway to reorganize this code to still be DRY but yet also<br>achieve more granular tests, despite their interdependencies?
<br><br>Thanks,<br><br>-Bryan</blockquote><div><br>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...
<br><br><span style="font-family: courier new,monospace;">context &quot;update cliient invalid data&quot; do<br>&nbsp; include ClientsControllerSpecHelper<br><br>&nbsp; specify &quot;should find client by id&quot; do<br>&nbsp;&nbsp;&nbsp; setup_controller
<br><br>&nbsp;&nbsp;&nbsp; @client = mock<br>&nbsp;&nbsp;&nbsp; Client.expects(:find).with(&quot;1&quot;).returns(@client)<br><br>&nbsp;&nbsp;&nbsp; @client.stubs(:attributes=)<br>&nbsp;&nbsp;&nbsp; @client.stubs(:save!).raises(ActiveRecord::RecordInvalid.new(stub(:errors =&gt; stub(:full_messages =&gt; []))))
<br><br>&nbsp;&nbsp;&nbsp; @controller.stubs(:render)<br><br>&nbsp;&nbsp;&nbsp; put :update, :id =&gt; 1, :client =&gt; &quot;attributes&quot;<br>&nbsp; end<br><br>&nbsp; specify &quot;should attempt to save client with new attributes&quot; do<br>&nbsp;&nbsp;&nbsp; setup_controller
<br><br>&nbsp;&nbsp;&nbsp; @client = mock<br>&nbsp;&nbsp;&nbsp; Client.stubs(:find).returns(@client)<br><br>&nbsp;&nbsp;&nbsp; @client.expects(:attributes=).with(&quot;attributes&quot;)<br>&nbsp;&nbsp;&nbsp; @client.expects(:save!).raises(ActiveRecord::RecordInvalid.new(stub(:errors =&gt; stub(:full_messages =&gt; []))))
<br><br>&nbsp;&nbsp;&nbsp; @controller.stubs(:render)<br><br>&nbsp;&nbsp;&nbsp; put :update, :id =&gt; 1, :client =&gt; &quot;attributes&quot;<br>&nbsp; end<br><br>&nbsp; specify &quot;should render edit form&quot; do<br>&nbsp;&nbsp;&nbsp; setup_controller<br><br>&nbsp;&nbsp;&nbsp; @client = mock
<br>&nbsp;&nbsp;&nbsp; Client.stubs(:find).returns(@client)<br><br>&nbsp;&nbsp;&nbsp; @client.stubs(:attributes=)<br>&nbsp;&nbsp;&nbsp; @client.stubs(:save!).raises(ActiveRecord::RecordInvalid.new(stub(:errors =&gt; stub(:full_messages =&gt; []))))<br><br>&nbsp;&nbsp;&nbsp; @controller.expects
(:render).with()<br>&nbsp;&nbsp;&nbsp; @controller.expects(:render).with(:action =&gt; &quot;edit&quot;)<br><br>&nbsp;&nbsp;&nbsp; put :update, :id =&gt; 1, :client =&gt; &quot;attributes&quot;<br>&nbsp; end<br><br>end<br></span><br>...admittedly not very DRY, but I don't think DRY-ness is as important as readability in tests.
<br><br>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...
<br><br><span style="font-family: courier new,monospace;">context &quot;update cliient invalid data&quot; do</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&nbsp; include ClientsControllerSpecHelper
</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&nbsp; </span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&nbsp; setup do
</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&nbsp;&nbsp;&nbsp; setup_controller</span><br style="font-family: courier new,monospace;"><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">&nbsp;&nbsp;&nbsp; @client = mock</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&nbsp;&nbsp;&nbsp; Client.stubs(:find).returns(@client)</span><br style="font-family: courier new,monospace;">
<br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&nbsp;&nbsp;&nbsp; @client.stubs(:attributes=)</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">
&nbsp;&nbsp;&nbsp; @client.stubs(:save!).raises(ActiveRecord::RecordInvalid.new(stub(:errors =&gt; stub(:full_messages =&gt; []))))</span><br style="font-family: courier new,monospace;"><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">
&nbsp;&nbsp;&nbsp; @controller.stubs(:render)</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&nbsp; end</span><br style="font-family: courier new,monospace;"><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">&nbsp; specify &quot;should find client by id&quot; do</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&nbsp;&nbsp;&nbsp; Client.expects
(:find).with(&quot;1&quot;).returns(@client)</span><br style="font-family: courier new,monospace;"><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&nbsp;&nbsp;&nbsp; put :update, :id =&gt; 1, :client =&gt; &quot;attributes&quot;
</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&nbsp; end</span><br style="font-family: courier new,monospace;"><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">
&nbsp; specify &quot;should attempt to save client with new attributes&quot; do</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&nbsp;&nbsp;&nbsp; @client.expects(:attributes=).with(&quot;attributes&quot;)
</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&nbsp;&nbsp;&nbsp; @client.expects(:save!).raises(ActiveRecord::RecordInvalid.new(stub(:errors =&gt; stub(:full_messages =&gt; []))))
</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&nbsp;&nbsp;&nbsp; </span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&nbsp;&nbsp;&nbsp; put :update, :id =&gt; 1, :client =&gt; &quot;attributes&quot;
</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&nbsp; end</span><br style="font-family: courier new,monospace;"><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">
&nbsp; specify &quot;should render edit form&quot; do</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">&nbsp;&nbsp;&nbsp; @controller.expects(:render).with()</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">&nbsp;&nbsp;&nbsp; @controller.expects(:render).with(:action =&gt; &quot;edit&quot;)</span><br style="font-family: courier new,monospace;"><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">&nbsp;&nbsp;&nbsp; put :update, :id =&gt; 1, :client =&gt; &quot;attributes&quot;</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">
&nbsp; end</span><br style="font-family: courier new,monospace;"><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">end</span><br><br><br>...I'm guessing this is more like what you're after. If yes, let me know and we'll look into making the change.
<br><br></div></div>-- <br>James.<br><a href="http://blog.floehopper.org">http://blog.floehopper.org</a>