tag:www.rhnh.net,2008:/ruby_forkRuby Fork - Xavier Shay's Blog2009-03-12T08:45:03ZEnkiXavier Shaynotreal@rhnh.nettag:www.rhnh.net,2008:Post/7962009-03-12T08:45:03Z2009-03-12T08:45:03ZFaster rails testing with ruby_fork<p>A long running test suite isn’t the problem. Your build server can take care of that. A second or two here or there, no one notices.</p>
<p><strong>The killer wait is in the red/green/refactor loop.</strong> You’re only running one or two tests, and an extra second can mean the difference between getting into flow or switching to twitter. And you know what kills you in rails?</p><table class="CodeRay"><tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt>5<tt>
</tt></pre></td>
<td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">$ time ruby -e '' -r config/environment.rb<tt>
</tt><tt>
</tt>real 0m3.784s<tt>
</tt>user 0m2.707s<tt>
</tt>sys 0m0.687s<tt>
</tt></pre></td>
</tr></table>
<p>Yep, the environment. That’s a lot of overhead to be waiting for everytime you run a test, especially since it’s the same code every time! You fix this with a clever script called <code>ruby_fork</code> that’s included in the <a href="http://zentest.rubyforge.org/ZenTest/">ZenTest</a> package. It loads up your environment, then just chills out, waiting. You send a ruby file to it, and it forks itself (the process containing the environment) to execute that file. The beauty of this is that forking is really quick, and it leaves a pristine copy of the environment around for the next test run.</p>
<p>‘Environment’ doesn’t just have be <code>environment.rb</code>, for bonus points you can load up <code>test_helper.rb</code>, which will also load your testing framework into memory. In fact, you can preload any ruby code at all – <code>ruby_fork</code> isn’t rails specific.</p><table class="CodeRay"><tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt>5<tt>
</tt>6<tt>
</tt>7<tt>
</tt>8<tt>
</tt>9<tt>
</tt><strong>10</strong><tt>
</tt>11<tt>
</tt>12<tt>
</tt>13<tt>
</tt></pre></td>
<td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">$ ruby_fork -r test/test_helper.rb &<tt>
</tt>/opt/local/bin/ruby_fork Running as PID 526 on 9084<tt>
</tt><tt>
</tt>$ time ruby_fork_client -r test/unit/your_test.rb<tt>
</tt>Started<tt>
</tt>...<tt>
</tt>Finished in 0.565636 seconds. # Aside: this time is bollocks<tt>
</tt><tt>
</tt>3 tests, 4 assertions, 0 failures, 0 errors<tt>
</tt><tt>
</tt>real 0m0.972s # This is the time you're interested in<tt>
</tt>user 0m0.225s<tt>
</tt>sys 0m0.035s<tt>
</tt></pre></td>
</tr></table>
<p>That’s fantastic, though you’ll notice in newer versions of rails your application code is not reloaded. By default your test environment caches classes – which normally isn’t a problem except that newer rails versions also eager load those classes (so they’re loaded when you load enviornment.rb). You can fix this by clearing out the eager load paths in your test environment file:</p><table class="CodeRay"><tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt>
</tt>2<tt>
</tt></pre></td>
<td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="c"># config/environments/test.rb</span><tt>
</tt>config.eager_load_paths = []<tt>
</tt></pre></td>
</tr></table>
<p>On my machine this gets individual test runs down from about 4 seconds to less than 1 second. You can sell that to your boss as a four-fold productivity increase.</p>