Cucumber is a BDD framework: you define the requirements of your software in plain text and then write tests that “execute” these requirements against the software. The difference from unit testing and TDD (where you write tests before code) is that the requirements are readable by non-technical people, and thus provide and executable documentation of how the application behaves. So it is not the code that is tested, but the application. For that, cucumber has integration with frameworks for in browser testing, httpunit etc. (see the cucumber source for many examples)

 

Cucumber is written in Ruby, it is interesting to note that it started as a port of a Java framework named Jbehave. The benefits of using Ruby is that tests are easy to write and the wealth of testing packages for testing various GUI frameworks. Using Jruby, one can write tests in Ruby that interact with Java classes (see example here)

 

Now cucumber comes full circle with cuke4duke, a bridge of cucumber to other languages on the JVM.

 

The project allows to run cucumber tests from an Ant or Maven build (read relevant documentation here and here). It also has API to write the tests in Java. Spring integration is also available.

 

 

The classic cucumber example for testing is one of a calculator.

 

Here are the requirements from a calculator (defined in features/addition.feature)

Feature: Addition
  In order to avoid silly mistakes
  As a math idiot 
  I want to be told the sum of two numbers
 
  Scenario Outline: Add two numbers
    Given I have entered <input_1> into the calculator
    And I have entered <input_2> into the calculator
    When I press add
    Then the result should be <output> on the screen
 
  Examples:
    | input_1 | input_2 | output |
    | 20      | 30      | 50     |
    | 2       | 5       | 7      |
    | 0       | 40      | 40     |

 

 

Note how the requirements do not define whether the calculator is tested as a service (API), GUI app or web app. (other requirements may specify that).

 

The step definition (first in Ruby) however, needs to know that (in this case testing a simple class):

require 'spec/expectations'

require 'cucumber/formatter/unicode'

require 'calculator'

 

Before do

   @calc = Calculator.new

end

 

Given /I have entered (\d+) into the calculator/ do |n|

   @calc.push n.to_i

end

 

When /I press (\w+)/ do |op|

   @result = @calc.send op

end

 

Then /the result should be (.*) on the screen/ do |result|

   @result.should == result.to_f

end

 

Cucumber will iterate over each example in the requirements and feed the values to the Given, When and Then sections.

 

Note that in this example, the calculator is written to be perfect for the test because we push two numbers and then send the operator. If on the other hand the calculator would have had only an add(num1,num2) method, then the test would have saved the values in local variables and invoke the method in the Then clause.

 

At this point we can use Cucumber to test Java code by running it with Jruby.

 

If we want to write the test in Java, then we create a Java file in src/test/java:

 

public class CalculatorSteps{

   private Calculator calc;

 

   private String result;

 

   @Before

   public void setup() {

     calc = new Calculator();

   }

 

   @Given("I have entered (\\d+) into the calculator")

   public void enterNumber(int n) {

     calc.push(n);

   }

 

   @When("I press (\\w+)")

   public void calculate(String op) {

     if ("add".equals(op)) {

       result = String.valueOf(calc.add());

       return;

      }

      throw new RuntimeException("Unknown operator " + op);

   }

 

   @Then("the result should be (.*) on the screen")

   public void checkResult(String n) {

     assertEquals(n, result);

   }

}

 

 

Some notes abou Maven:

  • The dependencies on cucumber related packages are defined in the 'test' scope (the documentation lists them as regular dependencies)
  • To work with other Ruby testing frameworks, their gems should be installed. Ruby gems are packages much like rpm or deb that can be installed site-wide so they become available to other packages. To add gems, use <gems> list in the plugin configuration. For example, to use Celerity (a jruby wrapper around HttpUnit), add:

<gems>

<gem>celerity:0.0.7</gem>

</gems>