Nick Sieger: RSpec, JRuby, Mocking, and Multiple Interfaces http://blog.nicksieger.com/articles/2006/12/01/rspec-jruby-mocking-and-multiple-interfaces en-us 40 RSpec, JRuby, Mocking, and Multiple Interfaces <p>The prospect of doing <a href="http://behaviour-driven.org/">behavior&#45;driven development</a> in Java has just taken a step closer with the news of <a href="http://www.infoq.com/news/2006/11/RSpecOnJRuby">RSpec running on JRuby</a>&#46; This is already a big step that will have an impact on Ruby and Java programmers alike in <a href="http://on-ruby.blogspot.com/2006/12/jruby-and-rspec-leftovers.html">a number of ways</a>&#46;</p> <p>However, it could be even better&#46; RSpec has a nice, intuitive mocking API, which will unfortunately, at the present time, be useless when working with java objects&#46; It would be awesome to try to get it to work, though&#46; Some possibilities:</p> <ol> <li>Map to <a href="http://www.jmock.org/">JMock</a> and use JMock under the hood&#46; Not a very attractive option for a number of reasons, but mainly because add&#45;on bridging layers are complex and should be avoided&#46;</li> <li>Improve ability for JRuby to implement any number of Java interfaces dynamically&#46;</li> </ol> <p>This second option is something <a href="http://headius.blogspot.com/">Charlie</a>, <a href="http://www.bloglines.com/blog/ThomasEEnebo">Tom</a> and I talked about on Tuesday night, that could have a much broader impact on Java integration in JRuby&#46;</p> <p>Consider this spec&#46; It&#8217;s trivial, but bear with me&#46;</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">context</span> <span class="punct">&quot;</span><span class="string">A TaskRunner</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="ident">setup</span> <span class="keyword">do</span> <span class="attribute">@task</span> <span class="punct">=</span> <span class="ident">mock</span><span class="punct">(&quot;</span><span class="string">Runnable</span><span class="punct">&quot;)</span> <span class="attribute">@task_runner</span> <span class="punct">=</span> <span class="constant">TaskRunner</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(</span><span class="attribute">@task</span><span class="punct">)</span> <span class="keyword">end</span> <span class="ident">specify</span> <span class="punct">&quot;</span><span class="string">runs a task when executed</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="attribute">@task</span><span class="punct">.</span><span class="ident">should_receive</span><span class="punct">(</span><span class="symbol">:run</span><span class="punct">)</span> <span class="attribute">@task_runner</span><span class="punct">.</span><span class="ident">execute</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>This spec might be satisfied by the following Java code:</p> <div class="typocode"><pre><code class="typocode_java ">public class TaskRunner { private Runnable task; public TaskRunner(Runnable r) { this.task = r; } public void execute() { task.run(); } }</code></pre></div> <p>Notice how I defined the <code>@task</code> in the spec above&#46; This is the normal way of <a href="http://rspec.rubyforge.org/documentation/mocks/mocks.html">mocking in RSpec</a>, and the example illustrates how I think JRuby should handle interfaces in Java: by duck&#45;typing them&#46;</p> <p>Basically, the RSpec mock should act like a Java Runnable because I&#8217;ve defined a <code>run</code> method on it (in this case implicitly with <code>@task.should_receive(:run)</code>)&#46; JRuby could wrap a dynamic invocation proxy around <strong>any</strong> Ruby object just before passing it into a Java method invocation&#46; Without doing any type&#45; or method&#45;checking up front&#46; Just define the proxy as implementing the interface required by the Java method signature, and let the JRuby runtime do its thing, and attempt to resolve methods as they&#8217;re invoked&#46; Possibly falling back to method_missing, even!</p> <p>Note that this would also make moot the <a href="http://headius.blogspot.com/2006/09/interface-implementation-syntax-and.html">multiple interface syntax discussion</a>, because you&#8217;d never have to declare an object in JRuby as implementing any particular interface&#46; Just define the appropriately named methods with the proper arity, and you&#8217;re done&#46; Maybe you don&#8217;t even need to declare all of them, if they never get called for your usage! This is the Ruby Way, and would be a completely natural extension to the way Java objects are manipulated in JRuby today, not to mention extremely concise and powerful&#46;</p> <p>This would also allow RSpec mocking to <em>just work</em>, at least for Java interface types, which would be way cool&#46;</p> <p>Charlie has a Swing demo that he frequently gives when <a href="http://tech.puredanger.com/2006/11/07/jruby/">talking about JRuby</a>&#46; Under the new proposal, it would look more like this:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">require</span> <span class="punct">'</span><span class="string">java</span><span class="punct">'</span> <span class="ident">frame</span> <span class="punct">=</span> <span class="ident">javax</span><span class="punct">.</span><span class="ident">swing</span><span class="punct">.</span><span class="ident">JFrame</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(&quot;</span><span class="string">Hello</span><span class="punct">&quot;)</span> <span class="ident">frame</span><span class="punct">.</span><span class="ident">setSize</span><span class="punct">(</span><span class="number">200</span><span class="punct">,</span><span class="number">200</span><span class="punct">)</span> <span class="ident">frame</span><span class="punct">.</span><span class="ident">show</span> <span class="ident">button</span> <span class="punct">=</span> <span class="ident">javax</span><span class="punct">.</span><span class="ident">swing</span><span class="punct">.</span><span class="ident">JButton</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(&quot;</span><span class="string">OK!</span><span class="punct">&quot;)</span> <span class="ident">frame</span><span class="punct">.</span><span class="ident">add</span><span class="punct">(</span><span class="ident">button</span><span class="punct">)</span> <span class="ident">frame</span><span class="punct">.</span><span class="ident">show</span> <span class="keyword">def </span><span class="method">actionPerformed</span><span class="punct">(</span><span class="ident">event</span><span class="punct">)</span> <span class="ident">event</span><span class="punct">.</span><span class="ident">source</span><span class="punct">.</span><span class="ident">text</span> <span class="punct">=</span> <span class="punct">&quot;</span><span class="string">Pressed!</span><span class="punct">&quot;</span> <span class="keyword">end</span> <span class="ident">button</span><span class="punct">.</span><span class="ident">addActionListener</span> <span class="constant">self</span></code></pre></div> <p>With luck, this approach will be coming to JRuby very soon&#46;</p> Fri, 01 Dec 2006 18:56:00 +0000 urn:uuid:b67283f4-b1e1-41e0-bb1a-794dd7135564 Nick Sieger http://blog.nicksieger.com/articles/2006/12/01/rspec-jruby-mocking-and-multiple-interfaces rspec jruby mockobjects http://blog.nicksieger.com/articles/trackback/167