tag:www.rhnh.net,2008:/ruby_fork Ruby Fork - Xavier Shay's Blog 2009-03-12T08:45:03Z Enki Xavier Shay notreal@rhnh.net tag:www.rhnh.net,2008:Post/796 2009-03-12T08:45:03Z 2009-03-12T08:45:03Z Faster rails testing with ruby_fork <p>A long running test suite isn&#8217;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&#8217;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&#8217;s a lot of overhead to be waiting for everytime you run a test, especially since it&#8217;s the same code every time! You fix this with a clever script called <code>ruby_fork</code> that&#8217;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>&#8216;Environment&#8217; doesn&#8217;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 &#8211; <code>ruby_fork</code> isn&#8217;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 &amp;<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&#8217;s fantastic, though you&#8217;ll notice in newer versions of rails your application code is not reloaded. By default your test environment caches classes &#8211; which normally isn&#8217;t a problem except that newer rails versions also eager load those classes (so they&#8217;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>