Since several weeks i planned to prepare an simple example for an upcoming talk about Acceptance Test Driven Development (ATDD). I decided to play with Cucumber as it seems to be the most adopted ATDD framework in the wild.
As Cucumber is a Ruby framework and i would like to use it in Java together with Maven I had a look at cuke4duke which should bring those 2 worlds together. I tried several examples but already failed to setup a very basic one. While looking around for an solution i came across an blog post from Thomas Sundberg. he pointed to the successor project of cuke4duke which is named cucumber-jvm. It's a pure Java implementation :-) of cucumber and supports several other languages like Ruby, Groovy, Python etc. From my point of view is has several advantages in contrast to cuke4duke:
- easy setup
- It took me less than 5 minutes to setup the basic example.
- faster
- cucumber-jvm provides a custom JUnit Runner to execute tests. No need to trigger maven to execute your integration test. Thus the startup time is drastically reduced.
- less magic
- Features and their implementation are linked via @Feature annotations. No need to scan your complete project anymore
As cucumber-jvm isn't available as stable version it must be resolved from a snapshot repo:
pom.xml:
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.ludeke.examples</groupId>
<artifactId>cucumber-jvm-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- JUnit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<!-- Cucumber -->
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-junit</artifactId>
<version>1.0.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-java</artifactId>
<version>1.0.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-picocontainer</artifactId>
<version>1.0.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.picocontainer</groupId>
<artifactId>picocontainer</artifactId>
<version>2.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>sonatype-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
</project>
CarMaintenance.feature:
Feature: Daily car maintenance
Cars need maintenance
Scenario: Fuelling
Given a car with 10 litres of fuel in the tank
When you fill it with 50 litres of fuel
Then the tank contains 60 litres
Car.java:
package net.ludeke.example;
public class Car {
private Integer fuelLevel;
public Car(int initialFuelLevel) {
fuelLevel = initialFuelLevel;
}
public void addFuel(int addedFuel) {
fuelLevel = fuelLevel + addedFuel;
}
public int getFuelLevel() {
return fuelLevel;
}
}
FuelCarTest.java:
package net.ludeke.example;
import cucumber.annotation.en.Given;
import cucumber.annotation.en.Then;
import cucumber.annotation.en.When;
import cucumber.junit.Cucumber;
import cucumber.junit.Feature;
import net.ludeke.example.Car;
import org.junit.runner.RunWith;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
@RunWith(Cucumber.class)
@Feature(value = "CarMaintenance.feature")
public class FuelCarTest {
private Car car;
@Given("^a car with (\\d*) litres of fuel in the tank$")
public void createCar(int initialFuelLevel) {
car = new Car(initialFuelLevel);
}
@When("^you fill it with (\\d*) litres of fuel$")
public void addFuel(int addedFuel) {
car.addFuel(addedFuel);
}
@Then("^the tank contains (\\d*) litres$")
public void checkBalance(int expectedFuelLevel) {
int actualFuelLevel = car.getFuelLevel();
assertThat(actualFuelLevel, is(expectedFuelLevel));
}
}
From my point of view this solution is much more straight forward for (at least) Java developers than cuke4duke.