RSpec, JRuby, Mocking, and Multiple Interfaces

Posted by Nick Sieger Fri, 01 Dec 2006 18:56:00 GMT

The prospect of doing behavior-driven development in Java has just taken a step closer with the news of RSpec running on JRuby. This is already a big step that will have an impact on Ruby and Java programmers alike in a number of ways.

However, it could be even better. RSpec has a nice, intuitive mocking API, which will unfortunately, at the present time, be useless when working with java objects. It would be awesome to try to get it to work, though. Some possibilities:

  1. Map to JMock and use JMock under the hood. Not a very attractive option for a number of reasons, but mainly because add-on bridging layers are complex and should be avoided.
  2. Improve ability for JRuby to implement any number of Java interfaces dynamically.

This second option is something Charlie, Tom and I talked about on Tuesday night, that could have a much broader impact on Java integration in JRuby.

Consider this spec. It’s trivial, but bear with me.

context "A TaskRunner" do
  setup do
    @task = mock("Runnable")
    @task_runner = TaskRunner.new(@task)
  end

  specify "runs a task when executed" do
    @task.should_receive(:run)
    @task_runner.execute
  end
end

This spec might be satisfied by the following Java code:

public class TaskRunner {
  private Runnable task;
  public TaskRunner(Runnable r) {
    this.task = r;
  }

  public void execute() {
    task.run();
  }
}

Notice how I defined the @task in the spec above. This is the normal way of mocking in RSpec, and the example illustrates how I think JRuby should handle interfaces in Java: by duck-typing them.

Basically, the RSpec mock should act like a Java Runnable because I’ve defined a run method on it (in this case implicitly with @task.should_receive(:run)). JRuby could wrap a dynamic invocation proxy around any Ruby object just before passing it into a Java method invocation. Without doing any type- or method-checking up front. 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’re invoked. Possibly falling back to method_missing, even!

Note that this would also make moot the multiple interface syntax discussion, because you’d never have to declare an object in JRuby as implementing any particular interface. Just define the appropriately named methods with the proper arity, and you’re done. Maybe you don’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.

This would also allow RSpec mocking to just work, at least for Java interface types, which would be way cool.

Charlie has a Swing demo that he frequently gives when talking about JRuby. Under the new proposal, it would look more like this:

require 'java'
frame = javax.swing.JFrame.new("Hello")
frame.setSize(200,200)
frame.show

button = javax.swing.JButton.new("OK!")
frame.add(button)
frame.show

def actionPerformed(event)
  event.source.text = "Pressed!"
end
button.addActionListener self

With luck, this approach will be coming to JRuby very soon.

Tags , ,  | no comments | no trackbacks

Comments

Trackbacks

Use the following link to trackback from your own site:
http://blog.nicksieger.com/articles/trackback/167