During our Agile Experience session with a client a couple of weeks ago, Chet and I were helping them with FitNesse for .NET. We thought it would be interesting to get FitNesse working with Ruby, so we've begun to work on that. Here, the results of that trial ... Now! With new explanations and graphics! See the end of the article.

You Keep on Downloading, and Downloading, and Downloading ...

On my machine, I began by downloading the current version of FitNesse from fitnesse.org. The version was 20060719. I unzipped that into my C:\fitnesse folder and tried launching. What came up looked like FitNesse, until we compared it to Chet’s. Mine was some older version. I’m still not sure what happened. We copied Chet’s fitnesse folder over the top of mine, and then it didn’t work at all. It seemed to be whining about something in Java. We knew that it needed Java 5 (apparently the cleverly-named version of Java whose number is 1.5.x.) I’m running an older version of Java, for compatibility with my web building software, lest I have to rework it. But we needed Java 5 for FitNesse. Searching my computer we found that I have JDK 1.5.something. So we changed the run.bat for FitNesse from

java -cp fitnesse.jar fitnesse.FitNesse %1 %2 %3 %4 %5
pause

to

"c:\Program Files\Java\jdk1.5.0_03\jre\bin\java" 
      -cp fitnesse.jar fitnesse.FitNesse -p 8088 %1 %2 %3 %4 %5
pause

It took a couple of tries, because all that first bit has to be in quotes, because Microsoft, in their wisdom, puts all the programs under a folder name that their own program, DOS, can’t properly understand. I had something on port 80, so we used 8088. When we did this, the FitNesse server comes up.

image

In text:

C:\fitnesse>"c:\Program Files\Java\jdk1.5.0_03\jre\bin\java" 
  -cp fitnesse.jar ftnesse.FitNesse -p 8088
FitNesse (20060719) Started...
        port:              8088
        root page:         fitnesse.wiki.FileSystemPage at ./FitNesseRoot
        logger:            none
        authenticator:     fitnesse.authentication.PromiscuousAuthenticator
        html page factory: fitnesse.html.HtmlPageFactory
        page version expiration set to 14 days.

Whee! Went into my browser, to localhost:8088, and got the front page of FitNesse. I didn’t have the Ruby stuff yet, was waiting for Chet for that. I ran the FitNesse tests, and most of them ran, but a few, having to do with Virtual something, didn’t. A note from Chet confirmed that his was the same, so we decided to ignore it. Keep ‘em all green, Uncle Bob, that’s my advice.

Gem is a Gem

To download the fit stuff for Ruby, you go to a command prompt and type “gem install fit” (or maybe it’s fit-1.1, I forget). Either way, gem is a gem. It looks on your hard drive, discovers you don’t have fit. Looks at the directory of what’s out on the web, discovers you’re out of date. Downloads the current directory, then goes and gets fit-1.1 and installs it just where it is supposed to go. Wow, perfect.

Now we just had to make it work.

Day Two, More or Less

Today, at the Brighton Borders, after an Iced Chai Latte to get me going, we hooked up our machines via VNC, and got to work. Chet had Ruby/FitNesse working, but we wanted to do it over again so that we could report, as well as possible, on what it took.

The FitNesse home page has a table of interesting things on it, and Chet had added a row pointing to RubyFitnesse and RubyFitNesseTests.

image

I picked that version of the home page up when we copied Chet’s files onto my system, so we didn’t have to type it in. In any case, the wiki source for the page now looks like this on my system:

!img-l https://files/images/FitNesseLogoMedium.jpg
!1 Welcome to [[FitNesse][FitNesse.FitNesse]]!
!3 ''The fully integrated standalone acceptance testing framework and wiki.''

|!c '''Table of Contents'''|
|!c [[A One-Minute Description][FitNesse.OneMinuteDescription]]|''What is [[FitNesse][FitNesse.FitNesse]]? Start here.''|
|!c [[A Two-Minute Example][FitNesse.TwoMinuteExample]]|''A brief example. Read this one next.''|
|!c [[User Guide][FitNesse.UserGuide]]|''Answer the rest of your questions here.''|
|!c [[Acceptance Tests][FitNesse.SuiteAcceptanceTests]]|''FitNesse's suite of Acceptance Tests''|
|!c [[Payroll System Tests][FitNesse.PayrollSystemSuite]]|Suite of Acceptance Tests for the Payroll System Exercise|
|!c [[Ruby Fitnesse][FitNesse.RubyTests]]|Ruby FitNesse Tests|

The page pointed to, FitNesse.RubyTests, looks like this:

image

And the wiki source is:

!define COMMAND_PATTERN {C:/Program Files/ruby/bin/ruby.exe -I %p -I 
    "C:/Program Files/ruby/lib/ruby/gems/1.8/gems/fit-1.1/lib" 
    "C:/Program Files/ruby/lib/ruby/gems/1.8/gems/fit-1.1/bin/FitServer.rb" -v}

!path C:\Program Files\ruby\lib\ruby\gems\1.8\gems\fit-1.1\lib\eg

I have built a test to exercise the example ^ArithmeticFixture.
----
Next I would like to do some simple tests to exercise the column fixture.  
These tests will use fixtures and model code that will be stored in the c:\data\ruby directory.

^ChetTests

Notable about these lines is that I had to put the paths from my system in there, as highlighted. We also had to fiddle with where the quotes had to be and where they didn’t. The rule is simple enough … if it’ll break some program’s line-end parsing, it needs quotes. In practice, what’s there works. We think there is some gimmickry with underbars and dashes that could be used to make things “simpler”. Maybe we’ll come back to that later.

Now what is supposed to happen with that define and the path statement is that all pages beneath this one will use that information to run their tests. So the ArithmeticFixture should work now.

image

Let’s look at its wiki source:

!|eg.ArithmeticFixture|
|x|y|+|-|*|/|
|1|1|2|0|1|1|

Not much to that. And Voila! It does work!

image

Chet's Test

The ArithmeticFixture runs because of code that is built into the Ruby fit installation. We need to learn how to do our own. Originally, Chet’s page looked like this:

image

And the source was just this:

!path C:\Documents and Settings\Ron\My Documents\Data\Ruby

Use a very simple fixture to make sure things work as I think they will.

!|Fitnesse1.Division|
|numerator|denominator| quotient()|
|2|1|2|
|60|30|2|

To make this link up and run, we have to learn some tricks. First, note that the name of the fixture is Fitnesse1.Division. If you’ll look at the path we show above, it comes down to Data\Ruby. Underneath that folder is the Ruby project I’m building, in a folder called Fitnesse1. That name is the same as the first name in the fixture. The fixture code itself looks like this:

require 'fit/column_fixture'

module Fitnesse1                  

  class Division < Fit::ColumnFixture 

    attr_accessor :numerator, :denominator

    def quotient()
      @numerator.to_f / @denominator.to_f
    end
  end

end

What I’m about to tell you is superstition: we have made it work this way, and we’re not sure how much flexibility we have. Maybe we’ll experiment later on. For now, what we think we know is that the module name, Fitnesse1, has to match the folder name, and the modifying name in the wiki page. The class name, Division, has to match the fixture name in the wiki page. Since that test shows inputs numerator and denominator, Chet coded them as member variables with accessors. The quotient method just returns the result of floating the two variables and dividing.

Again, there may be more flexibility than that. This is “cargo cult” programming right now, we just managed to make it work and we aren’t yet sure how much room there is to change things. But it does work:

image

Running Because We Can Walk

Up until now, we were just parroting what Chet had already figured out on his own. We were keen to move on. So we thought we’d try something with a row fixture.

I won’t go into the row fixture in great detail, but the basic idea is that a row fixture compares a collection in the fixture with a collection it gets from the system, and indicates what’s right and what’s not as right as it might be. We thought we’d do a little personnel database, as we often do. We added some code to Chet’s page:

!path C:\Documents and Settings\Ron\My Documents\Data\Ruby

Use a very simple fixture to make sure things work as I think they will.

!|Fitnesse1.Division|
|numerator|denominator| quotient()|
|2|1|2|
|60|30|2|

!|Fitnesse1.People|
|lastname|firstname|
|Hendrickson|Chet|
|Jefferies|Ron|

That makes it display like this:

image

Now aficionados of how I spell my name will recognize the extra e in the middle. It was our intention to make this test fail. We’ll show the code in a moment, but first let’s see it failing:

image

Sure enough, the test fails as we intended. (I didn’t think of myself as “surplus”, but there you have it.) Now let’s look at the Ruby code to make that happen. It’s pretty simple. In a file called people.rb, we have:

require 'fit/row_fixture'

module Fitnesse1

  class People < Fit::RowFixture
    def query
      p1 = Person.new("Chet", "Hendrickson")
      p2 = Person.new("Ron", "Jeffries")
      [p1, p2]
    end
  end

  class Person
    attr_accessor :lastname, :firstname
    def initialize(firstname, lastname)
      @lastname = lastname
      @firstname = firstname
    end
  end

end

Pretty straightforward. The fixture is a row fixture, and it’s named People, in the module named Fitnesse1. It implements a method called query, which is another little bit of cargo cult. Row fixtures will be sent the method “query”, to which they must return the collection of objects to be checked in the fixture.

In our case, we create two Persons (more about them in a moment) and return them in an array. The Person class has two accessors, lastname and firstname, which match the column names in the test. And when we create a person, he or she is initialized in the obvious way.

All this goes together so that the query method on People returns an array of two people, Chet and Ron, with Ron’s name spelled correctly. (Please make a note of that.) The test fails, and we discover the problem and change the code in the Fitnesse wiki:

!|Fitnesse1.People|
|lastname|firstname|
|Hendrickson|Chet|
|Jeffries|Ron|

And the test now runs!!

image

Let me explain. No, there is too much. Let me sum up.

What we have here is a fairly minimal working example of Ruby and FitNesse cooperating. So far we’re in cargo cult mode: it’s working but we’re not experts by a long shot. There were a lot of things we had to get right, and we’re not sure how delicate they are:

  1. Download FitNesse from fitnesse.org and get it correctly installed.
  2. Start the FitNesse server using Java 5. You might need to download this: I had a copy lying around.
  3. Go to a suitable FitNesse page and build a link to your Ruby test page.
  4. Set up the command information on your top level Ruby page. This includes a command pattern, and a classpath. From the look of the paths on the page, my intuition is telling me that there's more flexibility there than we used. We'll wiggle around a bit in a future article, perhaps.
  5. The FitNesse fixture gets a module name (optional, it seems, since the ArithmeticFixture example doesn't have one. (Note that its path points to ruby\eq, not to the ruby folder. That's a hint at one kind of option.)
  6. The module name appears to need to be in a module statement in ruby, and the class code needs to be in a folder of that same name.
  7. The fixture name is the name of the file in that folder (.rb, of course), and the class name equals the fixture name.
  8. The row fixture calls a method named "query" to get a collection of objects.
  9. The objects in the collection need to respond to the method names used in the column of the row fixture.

Whew! That’s a lot to cover in a couple of hours. Probably the Iced Chai Latte helped. What’s missing? Well, we don’t know, but here are some thoughts:

  • We haven't built any real model code. Right now, our fixtures are doing the work. Frankly, I like that fact. When I've seen teams using Fit or FitNesse, they often have built some object or objects, and start trying to build a fixture that interfaces to those objects. It can take a long time before the test runs. Working as we did, it's a lot like the "fake it till you make it" practice in Test-Driven Development. Our test runs ... it's just not talking to many real objects yet.
  • We need to learn more about fixtures. There aren't many fixtures available for Ruby Fit / FitNesse. If we get smarter than we are right now, maybe we can get involved in trying to create some. In any case, we need to learn what's there and how to use them.
  • We should take a look at how to organize tests and what to do with them. That will require us to figure out some little kind of application.
  • ... and probably much much more ...

The bottom line, though, is that with a couple of hours work, some of it very frustrating for Chet as he went through that phase where you type random things in, hoping one of them will work, we have Ruby and FitNesse talking with each other. We’re on the way.

We hope this article will help in two ways. First, we hope it’ll get you interested in FitNesse and/or Ruby if you aren’t already. Second, we hope it’ll help you get started with a little less trouble than you’d otherwise have.

With that in mind, if you do play with Ruby/FitNesse, and you have any feedback for this article, please drop me an email. As always, include the string [ron] as part of the subject to be sure of getting through my spam filters.

Thank you, and good night!


Now! New Explanations! Exciting New Graphics!

Donald Roby kindly pointed out that the reason the virtual tests fail in the Fitnesse suite is that we didn’t have a variable set correctly. When you change what port Fitnesse opens on, you need to change an entry on the FitNesse.SuiteAcceptanceTests page. It’s the port setting. For my machine, running on 8080, it should say:

!2 ''Port''
!define PORT {8088}
!define INTERNAL_PORT {9123}

Making this change fixes all the virtual tests, though I had to run the tests twice before one of the errors went away. I’m still left with the comment fixture not working, as is Chet. A noticeable improvement!

Also, I decided to put a Fitnesse starter icon on my desktop. Rather than let it have the standard DOS box icon, I made a Fitnesse icon. I just did the 32x32 size, not the three or four sizes you’re supposed to have for an official M$ icon. Anyway, here it is, download it if you care to:

image