As most of you know, Cucumber runs fine on the JVM via JRuby, and this opens
up the world of wonderful cukes for most of the languages on the JVM. There
is a lot of great innovation on the JVM. Scala, Clojure, Groovy and Ioke to
name a few are very interesting languages. And then there is Java of course.
Lots of people still use Java.
The same is true for the CLR/DLR (.NET) via IronRuby. C# is actually getting
pleasant (and confusing), F# is a wonderful language for functional/object
oriented programming similar to Scala.
The Cuke4Duke sister project of Cucumber has shown that JVM language
integration is relatively easy to implement. But until recently Cucumber
hasn't had a good API to plug in other programming languages than the
built-in support for the host language - Ruby. Cuke4Duke started as a proof
of concept, and I didn't pay much detail to API design. I just wanted to get
something working, so I hacked together a soup of monkey patches in Ruby.
Then people started using it :-)
There were several problems with this approach. Setup was clunky. People had
to add env.rb files to register their step definitions. Things were kind of
working with Java, but it turned out hard to leverage e.g. Groovy's dynamic
features so that Step Definitions could be written in a DSL similar to Ruby
Step Definitions. There was no way to use two programming languages side by
side. There was no clean way to have Cucumber print snippets in another
language etc etc.
All of these problems have now been solved. In order to achieve that I had
to refactor on both sides. Cucumber now has abstractions for 4 concepts:
Programming Language, World blocks, Step Definitions and Hooks. Cuke4Duke's
monkey patch soup code in Ruby has been replaced with Java implementations
of the new Cucumber Programming Language API. Translation of String and
primitive type arguments is handled transparently by JRuby. Table arguments
get converted back and forth via a Table java interface.
This means that you can write step definitions in Java (using @Given etc
annotations) and Groovy (using a Ruby-like DSL). Step Definitions in those
languages have access to almost all of the same Cucumber abstractions as in
Ruby. There are a few things missing, like calling Steps from Step
Definitions, Receiving a typed Scenario object in Hooks to name a couple,
but this is fringe functionality that can be added later.
Scala is coming up pretty soon, with Python to follow. The Twitter community
has helped with lots of great suggestions for a Scala DSL. I think I'm going
to go with something based on Dean Wampler's gist:
http://gist.github.com/161702 (James Strachan, author of Groovy likes it too
In the context of Cucumber and Language support I like think of 2 distinct
levels of interop. The simplest one is indirect interop. This is when you
talk to another language from Ruby Step Definitions. Exampes are JRuby and
rubypython, and this technique only relies on a 3rd party bridge from Ruby
to another language. There are examples of this in examples/java and
examples/python. Another example is Ian Dees' Cucumber on the iPhone, which
uses HTTP to send commands and retrieve results from the iPhone.
The other kind of interop is bidirectional. This means step definitions are
written in another language than Ruby, and Cucumber sends arguments into
those step definitions. This is typically done using the same kinds of 3rd
party bridges as for indirect iterop, but it's used in the both directions,
not just one. The iPhone approach could also be developed further so that
people can write Step Definitions in Objective C, testing iPhone apps or OS
X apps. Did I mention RubyCocoa?
I have pushed these changes to the programming_language branch in my
cucucumber repo. The refactorings shouldn't affect you unless you are using
some obscure internal API in Cucumber, but I want to give everyone the
chance to play with it before I merge it into master. Currently all the
features are passing, but there are currently 33 failing specs. It shouldn't
be too hard to fix these. One thing I know broke - Spork. I have fixed this
in my own spork fork and pushed it to GitHub. A new release of Spork () will
have to be made in order for Spork and Cucumber to work together.
I will be doing a rundown of all of this at the RubyFoo conference in London
in a couple of monts.
In the meanwhile I'm looking for feedback and patches ;-)