danlucraft /blog

Testing Ruby-GNOME2 applications with Cucumber

March 2009 • Daniel Lucraft

Testing Ruby-GNOME2 can be a tricky thing to get right. There have been all sorts of annoying issues regarding getting Test::Unit to work, and I know my own efforts with RSpec have not been entirely pain-free.

But getting Cucumber up and running to test Ruby-GNOME2 has been a joy. I think this is because unlike the other two, Cucumber doesn’t use Ruby’s at_exit to determine when to run its tests, giving you more control over the environment.

So here’s how. The problem with testing Ruby-GNOME2 - and indeed any gui - is that they are event based. Once you decide to use Ruby-GNOME2, you hand over control of your application to the gui main-loop. But test frameworks rely on keeping that control so they can dispatch tests.

For instance, you could decide to have a button in your application that reads “Run Tests”. And that button, when pressed, would fire a gui event that sends off to Test::Unit or whatever to say ‘run all tests’, and Test::Unit would march through its test suite, and then the original event would return control to the gui.

But all your tests have been processed within the same gui event handler. So by definition, no other events can have been processed during that time. That means no keypresses or menu clicks or TreeView updates will work in your tests.

What we want is for the gui to process events as needed, but still keep the control with our tests. We do that by replacing the GTK main-loop with a loop that we control.

First you have to write your application to not start the main-loop if features are running, as we will process our events manually. So your code will need to contain a guard like:

unless $running_features
  Gtk.main
end

Now in a Test::Unit testcase, you would need to write code like:

def test_click
  click_button
  process_all_gui_events
  check_dialog_visible
end

But in Cucumber, we can write something more natural. Our feature will look like this:

Feature: User clicks on something

Scenario: Single click
  When I click on the button
  Then the dialog should be visible

Run this feature using this Gtk Cucumber formatter (requires Cucumber 0.2):

module Cucumber
  module Formatter
    class GtkFormatter < Pretty
      
      def visit_step(step)
        super
        while Gtk.events_pending?
          Gtk.main_iteration 
        end
      end
    end
  end
end

And gui events will automatically be processed between each Cucumber step. Of course this means steps can’t rely upon events being processed within the body of the step, but steps are supposed to be short and atomic anyway, so it should not be a problem.

I have just started using this technique in Redcar, and it has been working very well (example). It’s much more comfortable than the RSpec version that I was using before. I’ll now use RSpec for unit testing in my application, with no gui interaction at all. This seems right because a test that requires the gui like this is an integration test, and Cucumber is our integration test framework!

blog comments powered by Disqus