<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>DogBiscuit</title>
    <description>... mmm, crunchy!</description>
    <link>http://dogbiscuit.org/mdub/weblog</link>
    <language>en-us</language>
    <generator>EvenYetAnotherWeblog</generator>
    <item>
      <title>Spying on your code with RR</title>
      <guid>http://dogbiscuit.org/mdub/weblog/Tech/Programming/Ruby/SpyingOnYourCodeWithRR</guid>
      <pubDate>Wed, 27 May 2009 22:39:00 +1000</pubDate>
      <description><![CDATA[<p>
A while back, Melbourne's own Pete Yandell created <a href='http://notahat.com/not_a_mock/'>Not A Mock</a>, an extension to RSpec that supports <a href='http://xunitpatterns.com/Test%20Spy.html'><em>test-spies</em></a>.  And a damn fine idea it was, too.
</p>
<p>
I've recently discovered that my current favourite stub/mock framework, Brian Takita's <a href='http://github.com/btakita/rr'>RR</a>, can do test-spies too!
</p>
<h3>
Huh?
</h3>
<p>
What's this "spy" business about?  Well, when <em>mocking</em>, <u>before</u> triggering the behaviour you're testing, you set up <em>expectations</em> that a certain methods of collaborating objects will be invoked, with the specified parameters.  Like so:
</p>
<pre>
describe TransferEverything do

  before do
    @account1 = Account.new
    @account2 = Account.new
    @transfer = TransferEverything.new(:from =&gt; @account1, :to =&gt; @account2)
  end

  describe &quot;#execute&quot; do

    it &quot;moves all funds from one account to the other&quot; do

      all_the_money = 1.42
      stub(@account1).balance { all_the_money }

      mock(@account1).withdraw(all_the_money)   # &lt;= set expectations
      mock(@account2).deposit(all_the_money)

      @transfer.execute                         # &lt;= execute
      
    end                                         # &lt;= verify expectations

  end

end
</pre>
<p>
The expectations are typically verified auto-magically, by the mocking framework, at the end of your test. 
</p>
<h3>
The spy alternative
</h3>
<p>
Setting up expectations <em>before</em> a call always feels clumsy.  Using a test <em>spy</em> makes tests flow more naturally:
</p>
<ol>
<li>
<strong>Stub</strong> out collaborators, setting up canned responses where required.
</li>
<li>
<strong>Execute</strong> the code you're testing.
</li>
<li>
<strong>Verify</strong> the results, including both:
</li>
<ul>
<li>
the outputs (return values, or resulting state)
</li>
<li>
the interactions (ie. the method-invocations you expected your fake collaborators to receive).
</li>
</ul>
</ol>
<p>
Fur egg-sample:
</p>
<pre>
describe TransferEverything do

  # ...

  describe &quot;#execute&quot; do

    it &quot;moves all funds from one account to the other&quot; do

      all_the_money = 1.42
      stub(@account1).balance { all_the_money }
      stub(@account1).withdraw
      stub(@account2).deposit

      @transfer.execute

      @account1.should have_received.withdraw(all_the_money)
      @account2.should have_received.deposit(all_the_money)

    end

  end

end
</pre>
<p>
One thing I find particularly useful about this technique is the ability to execute code in a setup block, then verify the various aspects of it's behaviour in separate test-cases.
</p>
<pre>
describe TransferEverything do

  # ...

  describe &quot;#execute&quot; do

    before do
      @all_the_money = 1.42
      stub(@account1).balance { all_the_money }
      stub(@account1).withdraw
      stub(@account2).deposit
      @transfer.execute
    end

    it &quot;withdraws all funds from source account&quot; do
      @account1.should have_received.withdraw(all_the_money)
    end

    it &quot;deposits funds in receiving account&quot; do
      @account2.should have_received.deposit(all_the_money)
    end

  end

end
</pre>
<p>
This results in smaller, more coherent test-cases.  
</p>
<h3>
Using RR test-spies in RSpec
</h3>
<p>
If you're using RSpec, you'll need to use the adapter class that comes with RR, rather than the one that comes with RSpec.  That is, in your <tt>spec_helper.rb</tt>, do this, which provides access to the <tt>have_received</tt> matcher.
</p>
<pre>
require 'rr'
Spec::Runners.configure do |config|
  config.mock_with RR::Adapters::Rspec
end
</pre>
<h3>
Spying on Java
</h3>
<p>
Honourable mention: if you're lucky (*cough*) enough to be coding Java, I HIGHLY recommended <a href='http://mockito.org'>Mockito</a>, which also implements test-spies, and is easily the best Java mocking/stubbing library around.
</p>
]]></description>
    </item>
  </channel>
</rss>
