Crouching Donny, Iron Cucumber – An Experiment in Functional Testing

work safe

I accidentally agreed to give a talk later this month. The subject is IronRuby and it’s application in the .NET ecosystem. Seems simple enough. So, to jazz things up, I want to have a host of demo applications. This is about one.

A while back, I posted an article with an example of using IronRuby and the White framework. In it, I appear to have made a promise.

I’ll get to Cucumber. I’ll publish examples. I promise.

I’m going to talk a lot about Donny. You’ll meet Donny, but just be aware, he’s compiled in Debug, so he’s a bit shy. Donny is an app written to give me something to test. He does nothing of real import, he doesn’t even save his data to disk. As for why “Donny”, I still don’t know. I was in a mood yesterday when outlining my presentation and “Donny” stuck.

Cucumber – n, A functional testing framework for ruby. Cucumber uses Features and Step Definitions to drive an application via it’s full stack.

Cucumber does work with IronRuby, but by default it’s runner has some idiosyncrasies for which you’ll need to compensate. The cucumber.bat file lives, for me, in c:\IronRuby\lib\IronRuby\gems\1.8\bin. Another oddity is that it belives I have a color console. This is mostly true, but Win32Console doesn’t work for IronRuby, so you’ll need to use the --no-color option. For now. I have my features in a directory I’m calling “features” with an embedded step definitions directory. Once I finish the presentation, I’ll have the whole damn thing published and you can look at it then. Until then, here’s the command line I used; cucumber --no-colors features.

Feature – n, A tightly formatted Business Natural Language used to describe the preconditions, actions, and expected results in a business scenario. See also, Story.

I have two basic scenarios; Donny starts with no data, and Donny moves data from the task form to the task list.

Feature: New Task
  In order to correctly bill my client
  as an SEP employee
  I want to add a task to the list

  Scenario: Starting the app
    Then all fields are blank
    And the list contains 0 entries

  Scenario:  Add a new Task
    Given I describe my task as Doing work on the Donny app
    And I'm working on project Academy
    And the day is Monday
    And I worked from 10 to 10:30
    When I press the button
    Then all fields are blank
    And the list contains 1 entry
    And the last entry is Monday 10-10:30 Academy Doing work on the Donny app 

Step Definition – n, a ruby script used to match lines in the Feature BNL to actual executable code. They can be reused across features.

For each Given, When, or Then I have to have a matching step. These are essentially ruby functions that use a helper to encapsulate my usage of the White library. Here is donny_steps.rb

require 'spec/expectations'
require File.dirname(__FILE__) + "/../../donny_helper.rb"

Before do
  @app = Application.launch "Donny/bin/Debug/donny.exe"
  @win = @app.get_window "Window1"
  @tf = TaskForm.new(@app, @win)
  @tl = TaskList.new(@app, @win)
end

After do
  @win.close
end
Given /I describe my task as (.*)/ do |description|
  @tf.description = description
end

Given "I'm working on project $proj" do |proj|
  @tf.project = proj
end

Given "the day is $day" do |day|
  @tf.day = day
end

Given /I worked from (.*) to (.*)/ do |start_time, end_time|
  @tf.start_time = start_time
  @tf.end_time = end_time
end

When "I press the button" do
  @tf.commit
end

Then "all fields are blank" do
  @tf.description.should be_empty
  @tf.project.should be_empty
  @tf.day.should be_empty
  @tf.start_time.should be_empty
  @tf.end_time.should be_empty
end

Then /the list contains (.*) entr(?:y|ies)/ do |n|
  @tl.task_count.should == n.to_i
end

Then /the last entry is (.*)/ do |result|
  @tl.tasks.last.text.should == result
end

Um, you’re missing a piece. The part that uses White. Remember the screens? This time, I didn’t call them screens, and I don’t have a full window proxy, but I still have two helper objects. Now, with some metaprogramming, I could cut down the code size by defining an attribute and describing the type and automation ID of it’s associated control, but I’m lazy. Maybe for the presentation, but I’m not making any promises.

white_loc = (File.dirname(__FILE__) + "/White_Bin_0.18/")
puts white_loc
$LOAD_PATH << white_loc
require "White.Core.dll"

Application = Core::Application
ComboBox = Core::UIItems::ListBoxItems::ComboBox
ListBox = Core::UIItems::ListBoxItems::ListBox
Window = Core::UIItems::WindowItems::Window
include Core::UIItems

class Window
  def get_button(*args)
    self.method(:get).of(Button).call(*args)
  end
  def get_textbox(*args)
    self.method(:get).of(TextBox).call(*args)
  end
  def get_combobox(*args)
    self.method(:get).of(ComboBox).call(*args)
  end
  def get_listbox(*args)
    self.method(:get).of(ListBox).call(*args)
  end
end
class TaskList
  def initialize(app, win)
    @app = app
    @win = win
  end
  def tasks
    @win.get_listbox('tasklist').items.to_a
  end
  def task_count
    tasks.size
  end
end
class TaskForm
  def initialize(app, win)
    @app = app
    @win = win
  end
  def description
    get_tb 'task'
  end
  def description=(text)
    set_tb 'task', text
  end
  def project
    @win.get_combobox('project').selected_item_text
  end
  def project=(proj)
    @win.get_combobox('project').select proj
  end
  def day
    get_tb 'dayofweek'
  end
  def day=(day)
    set_tb 'dayofweek', day
  end
  def end_time
    get_tb 'endtime'
  end
  def end_time=(et)
    set_tb 'endtime', et
  end
  def start_time
    get_tb 'starttime'
  end
  def start_time=(st)
    set_tb('starttime', st)
  end
  def commit
    @win.get_button('commit').click
  end
  
  private
  def set_tb(auto_id, value)
    @win.get_textbox(auto_id).set_value value
  end
  def get_tb(auto_id)
    @win.get_textbox(auto_id).text
  end
end

And some for some context, Meet Donny!
Donny

And, after I enter a task, Donny looks like this.
Donny

That's really all there is to using Cucumber with IronRuby. Have fun!

1 Comment

No Comments

1 Trackback

Leave a Reply

Allowed tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>