Asymmetrical View

How We Run Cucumber

Cucumber is a wonderful behavior driven development tool by Aslak Hellesøy. There is a getting started guide in the wiki at github that describes how to get it set up and running.

We’ve written a wrapper around Cucumber to help make it easier to run along with our application and to make it less intrusive when it runs the browser (at least under Linux).

Server Execution

Most of our test driven development doesn’t require script/server to be running the web application, and we want it to run under either the cucumber or test rails environment when it does. Stopping (if it’s running) the development server, starting it and then shutting it down again when the tests are done was a manual process that we automated.

Xnest

When we run Cucumber under X Windows, it often grabs focus and keeps us from doing anything else with the desktop while the tests are running. This is annoying to say the least. Thankfully X11 has a utility called Xnest which allows you to run a window which is a contained X desktop. Running this and then telling the browser to run inside it keeps it out of our way and allows us to keep using the machine without being constantly interrupted by the browser constantly jumping to the foreground.

script/server Arguments

To pass arguments (such as the port, or other options) through to script/server, put a -- between the arguments to script/cucumber-runner and they will be passed on verbatim to script/server. In this example, -p 8080 will be passed to script/server instructing it to use port 8080.

$ script/cucumber-runner -g 1024x768 -- -p 8080

cucumber-runner

#!/usr/bin/env ruby
require 'optparse'

# Controller for running Cucumber feature tests on your Rails application.
# Supports stopping/starting WEBRick in cucumber or test rails environment and
# (on supporting systems, eg: Linux) running the browser under Xnest to keep it
# away from your other desktop activities.
#
#
# Author::     Kyle Burton <kyle.burton@gmail.com>
# Copyright::  Copyright (c) 2010 Kyle Burton
# License::    Distributes under the same terms as Ruby

#
# Command line runner.
#
class CucumberRunner

  # location of the rails scripts
  SCRIPT_ROOT = File.dirname(__FILE__)

  # Set up defaults, can be overridden with command line parameters.
  def initialize
    @opts = {
      :env             => 'cucumber',
      :geometry        => '1280x800',
      :display         => ENV['DISPLAY'],
      :xnest_display   => ':2',
      :control_webrick => true,
      :try_use_xnest   => true
    }
  end

  # Determine if Xnest is present, and if it should be used (Not on OS X).
  def use_xnest?
    return false unless @opts[:try_use_xnest]
    on_darwin = (`uname` =~ /Darwin/)
    has_xnest = !(`which Xnest`.empty?)
    !on_darwin && has_xnest
  end

  # Start Xnest in the background, save off the pid so it can be stopped later.
  def start_xnest
    @opts[:display] = @opts[:xnest_display]
    cmd = "Xnest #{@opts[:xnest_display]} & echo $!"
    @xnest_pid = `#{cmd}`.to_i
  end

  # Stop Xnest with a SIGTERM.
  def stop_xnest
    Process.kill("TERM", @xnest_pid) if @xnest_pid
  end

  # Stop the WEBRick server by looking for it in the process tree.
  def stop_webrick
    res = `ps aux | grep [r]uby | grep [s]cript/server`
    pid = res.split[1]
    return unless pid
    puts "#{$0} STOPPING WEBRick pid=#{pid}"
    Process.kill "KILL", pid.to_i
  end

  # Start WEBRick with the configured rails environment as a daemon (background).
  def start_webrick
    cmd = "#{SCRIPT_ROOT}/server -e #{@opts[:env]} -d"
    puts "#{$0} Starting WEBRick: #{cmd}"
    system cmd
  end

  # Process any command line arguments.
  def parse_opts
    OptionParser.new do |opts|
      opts.banner = "Usage: #{$0} [options]"

      opts.on("-h", "--help") do
        puts <<-EOH
       #{$0} [[opts]]

        -e ENV  --envrionment ENV   Rails environment to run under default=#{@opts[:env]}
        -g GEO  --geometry GEO      Xnest geometry to use default=#{@opts[:geometry]}
        -X      --no-xnest          Do not use Xnest (even if present)
        -d DSP  --display DSP       DISPLAY to use default=#{@opts[:display]}
        -x DSP  --xnest-display DSP Xnest's DISPLAY to use default=#{@opts[:xnest_display]}
        -W      --no-webrick        Don't start WEBRick (assume running) default=false
        EOH
        exit 0
      end

      opts.on("-e", "--envrionment ENV") do |env|
        @opts[:env] = env
      end

      opts.on("-g", "--geometry GEO") do |geo|
        @opts[:geometry] = geo
      end

      opts.on("-X", "--no-xnest") do
        options[:use_xnest] = false
      end

      opts.on("-d", "--display DSP") do |dsp|
        options[:display] = dsp
      end

      opts.on("-x", "--xnest-display DISP") do |disp|
        options[:xnest_display] = disp
      end

      opts.on("-W", "--no-webrick") do
        options[:control_webrick] = false
      end
    end.parse!
  end

  # Process options, start Xnest, [re]start WEBRick, execute the cucumber
  # suite, then stop Xnest and stop WEBrick
  def run
    parse_opts
    start_xnest if use_xnest?

    if @opts[:control_webrick]
      stop_webrick
      start_webrick
    end

    cmd = "#{SCRIPT_ROOT}/runner #{SCRIPT_ROOT}/cucumber #{ARGV}"
    ENV['DISPLAY']   = @opts[:display]
    ENV['RAILS_ENV'] = @opts[:env]
    puts "#{$0} using RAILS_ENV=#{ENV['RAILS_ENV']}"
    res = system cmd

  ensure
    stop_webrick  if @opts[:control_webrick]
    stop_xnest    if @xnest_pid
  end

  def self.main
    self.new.run
  end
end

CucumberRunner.main

You can download or check out cucumber-runner from my github cucumber-example project.

Running Cucumber with this wrapper helps make it easier and less intrusive to use. If anyone has suggestions for how to make it less intrusive while running under OS X, please email me, I’d love to hear your suggestions.

Kyle Burton, 10 Jun 2010 – Philadelphia PA


Tags: cucumber,ruby,testing