I’m a big fan of what Cucumber has to offer a .NET developer like myself. What gets me down is the problem that IronRuby never seems *quite* mature enough to run it.

I’ve encountered (what i believe to be) a bug in IronRuby 0.9.0.0. The following feature (with no .NET interop) works using the Ruby interpreter, but fails under IronRuby.

The specification to reproduce the bug is based on a simple calculator performing an addition task. IronRuby barfs with the following error:

D:sourcerubycalculator>icucumber features
Feature: Addition
In order to save time
as a math n00b
I want to be able to add 2 numbers

wrong number of arguments (1 for 0) (ArgumentError)
c:/Ruby/lib/ruby/gems/1.8/gems/cucumber-0.3.94/bin/../lib/cucumber/ast/feature_element.rb:24:in `name_line_lengths’
:0:in `send’
IronRuby.Libraries:0:in `SendMessageOpt’
:0:in `each’
c:/Ruby/lib/ruby/gems/1.8/gems/cucumber-0.3.94/bin/../lib/cucumber/ast/feature_element.rb:20:in `first_line_length’
IronRuby.Libraries:0:in `Each’
:0:in `collect’


Hopefully the problem turns out to be something weird in my environment, but I’m reproducing the code below in case someone else would like to try it:

(i’ve also pushed it to Github: Ruby Calculator)

featuresaddition.feature
Feature: Addition
	In order to save time
	as a math n00b
	I want to be able to add 2 numbers

Scenario: Addition
	Given I have entered the first number 2 into the calculator
	And I have entered the second number 3 into the calculator
	When I call the add method
	Then the result 5 should be returned

Scenario Outline: More Addition
	Given I have entered the first number  into the calculator
	And I have entered the second number  into the calculator
	When I call the  method
	Then the result  should be returned

Examples:
	| x  | y | method | result |
	| 2  | 3 | add    | 5      |
	| 1  | 1 | add    | 2      |
	| 0  | 0 | add    | 0      |
	| -2 | 3 | add    | 1      |

 

featuresstep_definitionscalculator_steps.rb
require "spec"
require "lib/calculator"

Before do 
	@calc = Calculator.new
end

Given /^I have entered the first number (-?d+) into the calculator$/ do |n|
  @x = n.to_i
end

Given /^I have entered the second number (-?d+) into the calculator$/ do |n|
	@y = n.to_i
end

When /^I call the add method$/ do	
	@result = @calc.add(@x, @y)
end

Then /^the result (d+) should be returned$/ do |n|
  @result.should equal(n.to_i)
end
libcalculator.rb

class Calculator
	def add(x, y)
		return x+y
	end
end

Not long ago, I posted a big fat blat of information from my investigation in trying to get Ruby based spec testing integrated with .NET. In this post, I make some sense of all that content and (more importantly) drop a sample of taking advantage of this. (nb: This post is essentially a direct-rip of an internal document I created for this purpose)

Overview

The purpose of this page is to run through a process which will ultimately allow the reader (thats you) to write Ruby based specifications for your .NET code.

Why?

Why would you want to do this? The intended purpose for this practice is to gain the most benefit when doing BDD. Trying to do BDD in C# results in a lot of syntactical noise in the code which distracts from the goal of having clear, readable specifcations of how the intended function should behave. Additionally, any traditional C# BDD toolset requires the specifications to be statically compiled into a test binary in order to be executed. The advantage of using Ruby is that the scripted nature of the language allows physical (as well as logical) separation of speficiations from code, opening up the realm of possibility that specifications are written by non-technical folk. Furthermore, the Ruby syntax lends itself to building DSLs perfect for the purpose of allowing clean, almost human-readable code.

Scope of investigation

The investigation work preceding this post was set 3 goals:

  • Determine the viability of using Cucumber as an automated feature verification utility
  • Determine the viability of using RSpec as an automated specification verification utility
  • Determine the viability of using IronRuby as conduit to allow Ruby specificaitons to execute against compiled C# code. (applicability to any other CLR-supported language is then assumed).

Investigation results

The results of the investigation showed that:

  • RSpec is currently not supported on IronRuby due to a number of bugs in the IronRuby project. (Based upon a discussion with @jschementi and this article (toward the bottom))
  • Accordingly, Cucumber is currently not supported on IronRuby due to it using RSpec internally (explained onthe ruby forums)
  • The IronRuby team have worked to incorporate support for a more lean specification testing tool MSpec which is very similar in syntax to RSpec, but not as functionally complete.
  • MSpec will work with IronRuby to write Ruby based specifications to verify .NET compiled applications.

What tools are we using?

Based on the results of the investigation, the best way to approach this method of testing is to use the MSpec library to write specifications against C# code, execute them using IronRuby and in future, once IronRuby is more stable we can look to migrating over to cucumber for feature-style verification on top of M/RSpec.

RubyGems

RubyGems is a package which allows you to download ruby components and utilities (known as Gems). The default RubyGems package which comes with the one-click installer might be outdated when you download it, so the best thing to do here is to update RubyGems to the latest version

gem update –system

In the event you’re behind a company firewall, or you need to use an HTTP proxy for whatever reason, you need to tell the GEM command to use the http proxy as it doesn’t honour your default internet options. Substitute the server and port where appropriate and then run the gem update:

SET HTTP_PROXY=http://your.proxy.server:3128

You’re now minty fresh with the latest RubyGem package.

IronRuby (IR)

Go and download the latest release of the IronRuby project from ironruby.net. The current “official” pre-release release is v0.3, and it doesn’t have any installer. To “install”, make sure you extract the contents to the location

c:ironruby*

This is the standard installation location for IR. Once there, it’s recommended that you update your system path to include the path your IR’s bin folder.

Setting PATH Environment for IronRuby

Setting PATH Environment for IronRuby


Required Gems

We now get to the part where you need to install some of the gems required for specification testing. As mentioned at the start, RSpec and Cucumber isn’t 100% working with IR just yet, however it’s worthwhile installing them anyway to test things are working as expected and whatnot.

gem install mspec
gem install cucumber
gem install win32console
##gem install rspec
##gem install hoe

The last 2 should automatically be installed when you install cucumber as they’re dependencies, but if they don’t make sure you install them! If you really want to keep it lean you can get away with just mspec and none of the others.

Now the standard install of IR has its own repository of gems which can be managed thorugh IR’s igem utility. The reason we don’t use IR’s gem utility to install mspec is because mspec is a pretty special script which (basically) allows us to tell it which ruby interpreter we wish to use for running tests (explanation, thanks to @jredville). The neato thing here is that we then don’t need to install mspec speficially for IR, we can repurpose the MRI’s version.

Testing it out

Now that all the major stuff is installed lets test it out by creating a simple app in C# and write a specification in Ruby to verify its behaviour. Create a new folder to save the source files in.

using System;

namespace HelloWorld
{
    public class HelloClass
    {
        public string SayHello()
        {
            return "Hello from C#";
        }
    }
}

Here we have a class which returns a string when the method SayHello() is invoked.

require "mspec"
require "HelloWorld.dll"

describe "the hello dot net app" do 
	before do 
		@app = HelloWorld::HelloClass.new
	end

	it "should say hello from c#" do 
		@app.say_hello.to_s.should == "Hello from C#"
	end
end

This is our specification for the behaviour of the application.

To compile the C# class, open up a Visual Studio Command Prompt, CD to the source directory and type

csc /target:library /out:HelloWorld.dll HelloWorld.cs

…and now to run this puppy:

mspec -t c:ironrubybinir.exe sayhello_spec.rb

Here, we are invoking the mspec ruby script and passing two arguments. The first -t c:ironrubybinir.exe tells the ruby script that we with to execute the mspec specifications using a different Ruby interpreter to MRI. The interpreter we want to use in this case is IronRuby. The second argument tells it which spec we’re running. When mspec runs, it finds the -t argument and hands-off execution of the spec to another instance of mspec executing under IronRuby. This gives us the flexibility of being able to execute standard ruby specs and also calling out to IronRuby for .NET interop if needed.

The observant of you might notice that the call to @app.say_hello has a to_s chained afterward. IronRuby will return a ClrString as the object type when the interop call returns a CLR type string. the CLR’s ClrString and Ruby’s string are not interchangeable. You need to call to_s on the ClrString to treat it like a ruby string. This behaviour is at least explained, albeit I need to dig deeper to understand why they couldn’t have an implicit cast operator (or dynamic language equivalent thereof).

One thing that’s important to note is that although i’ve dropped a few source files without too much explanation, you would actually build this up iteratively using the same TDD testing style you’ve always been used to. In fact, this form of specification testing makes test-first easier to do.

Further work

This article gives a straightforward overview of how to begin testing C# code with Ruby but it doesn’t go all the way.

  • Ideally we would like to use Cucumber for automated feature acceptance test verification. Unfortunately the current build of IronRuby doesn’t work with Cucumber and RSpec but there should be ways to get the current IR implementation to work with a few tweaks.
  • Need to define and configure a standard project skeleton such that you don’t need to download and extract IR in order to get the system working. In a perfect situation, we could download only the source for the software without requiring any dependencies installed (including ruby!).

Coming with no experience in both Ruby or IronRuby, this is just a big dump of my thoughts on the topic as i make my way around to learn them both and bring them into direct use within our project.

  • Most frustrating thing i want to say upfront and get out of the way now – almost all documentation i’ve found for Ruby (the language) is discussed in the context of using Rails. Whilst this might be true for 95% of Ruby users, it frustrates the poo-poo’s out of me. Rails is quite obviously a very advanced framework, and it really helps blur the distinction between what is “Ruby” and what is “Rails”. Particuarly to do with shortcuts provided by the rails framework I haven’t found elsewhere (for example, ruby script/generate rspec)
  • Installing Ruby is as difficult as downloading the installer for Windows and running it. That’s where my degree pays for itself.

  • Of course depending on how old the download package you’re using is, you’ll need to update your gems to the latest version with gem update –system

  • In the event you’re behind a company firewall, or you need to use an HTTP proxy for whatever reason, you need to tell the GEM command to use the http proxy as it doesn’t honour your default internet options. SET HTTP_PROXY=http://your.proxy.server:3128, substituting the server and port where appropriate.

  • When it comes time to play with IronRuby, you’ll need to install a few gems which didn’t seem to come with the pre-built version of the assembly.

    Firstly, make sure you put your IronRuby bin directory into the path. If you want to use RSpec (which you do), then you’ll need to igem install rspec and igem install hoe. if you do igem install cucumber you should get both for free.

    Secondly, it seems like recent changes to the IR project have broken the implementation of the expand_path method. Although there is claimed to be an available workaround, I have not had any luck thusfar with it. (Will try recompiling from source). A quick chat with @jschementi confirms there is something broken with the current build and will be fixed soon.

  • mspec works, though you might have some complications with getting mspec to use the right ruby interpreter. big props go out to @jredville for helping me sort that one out. If you run mspec by itself or try to invoke it directly through IR, you will invariably end up with an error like this:

    c:sourcerubycalcdotnet>imspec spec
    ruby 1.8.6 (2007-09-24 patchlevel 111) [i386-mswin32]

    1) An exception occurred during: loading c:/source/ruby/calcdotnet/spec/calculator_spec.rb ERROR
    LoadError: 127: The specified procedure could not be found. – Init_calculator
    c:/source/ruby/calcdotnet/lib/calculator.dll
    c:/source/ruby/calcdotnet/lib/calculator.dll
    c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require’
    c:/source/ruby/calcdotnet/spec/calculator_spec.rb:1

    Finished in 0.090000 seconds

    1 file, 0 examples, 0 expectations, 0 failures, 1 error

    The trick here is that mspec is designed to allow itself to swap one copy out for another. If you pass mspec the argument “-t c:ironrubybinir.exe” it will use IR instead of the ruby interpreter. eg: mspec -t c:ironrubybinir.exe spec/