Cucumber und JUnit 5 sind beides beliebte Testing-Frameworks in der Java-Welt. Die Integration beider war aufgrund der unterschiedlichen Konzepte in der Vergangenheit schwierig. Inzwischen unterstützt die neu entwickelte cucumber-junit-platform-engine das Ausführen von Cucumber-Szenarien mittels JUnit 5 fast out-of-the-box. In diesem Artikel zeigen wir, wie Cucumber mit JUnit 5 ausgeführt werden kann und verwenden dafür Gradle in der Kotlin-Notation.

Cucumber

In sogenannten Feature-Files wird die Funktionalität eines Systems beschrieben. Die dabei verwendeten Wörter und Sätze sollten in einer typischen Diskussion innerhalb des Teams vorkommen, wenn das entsprechende Feature besprochen wird. Testszenarien sehen ungefähr wie folgt aus:

Feature: Guess the word

  Scenario: Maker starts a game
    Given the game is setup
    When the Maker starts a game
    Then the Maker waits for a Breaker to join

Jedes Szenario wird durch eine Reihe an Schritten beschrieben, die mit Keywords wie Given, When und Then beginnen. Um diese Schritte auszuführen, muss sogenannter Glue-Code geschrieben werden:

package your.own.steps;

import io.cucumber.java.en.When;
import io.cucumber.java.en.Then;

public class YourSteps {

	@Given("the game is setup")
	public void setup() {
		// execute step
	}

	@When("the Maker starts a game")
	public void someStep() {
		// execute step
	}

	@Then("the Maker waits for a Breaker to join")
	public void otherStep() {
		// execute other step
	}

}

Damit Testszenarien ausgeführt werden können, müssen sowohl Feature-Files also auch der Glue-Code gefunden werden.

JUnit 5

Einer der größten Stärken von JUnit 5 im Vergleich zu seinem Vorgänger ist die Erweiterbarkeit. Das Cucumber-Team bedient sich dieser Erweiterbarkeit in der eingangs erwähnten platform-engine, um sowohl Testszenarien als auch Glue-Code zu finden, und anschließend die Testausführung an JUnit zu delegieren. Neben einer zusätzlichen Abhängigkeit muss nur noch eine Klasse mit @Cucumber annotiert werden, um die Integration abzuschließen.

package your.own;

import io.cucumber.junit.platform.engine.Cucumber;

@Cucumber
public class RunCucumberTest {
}

Gradle

Wir benutzen Gradle, um alle notwendigen Abhängigkeiten zu deklarieren und die Testausführung zu konfigurieren. Das folgende Beispiel zeigt ein minimales Setup in Kotlin-Notation, für ein Projekt welches ausschließlich Cucumber-Szenarien beinhaltet und diese ausführen möchte.

plugins {
  java
}

dependencies {
  testImplementation("org.junit.jupiter:junit-jupiter-api:latest.release") {
	because("we want to use JUnit 5")
  }
  testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:latest.release") {
	because("Cucumber relies on jupiter-engine to resolve tests")
  }
  testImplementation("io.cucumber:cucumber-java:latest.release") {
	because("we want to use Cucumber JVM")
  }
  testImplementation("io.cucumber:cucumber-junit-platform-engine:latest.release") {
	because("we want to use Cucumber with JUnit 5")
  }
  testImplementation("io.cucumber:cucumber-picocontainer:latest.release") {
	because("we want to use dependency injection in our Cucumber tests")
  }
}

tasks {
    test {
        systemProperties(System.getProperties().toMap() as Map<String, Any>)
        systemProperty("cucumber.execution.parallel.enabled", System.getProperty("test.parallel"))
        systemProperty("cucumber.filter.tags", "not @ignore")
        useJUnitPlatform {
            excludeTags("disabled")
        }
    }
}

Einmal eingerichtet, reicht ein einfaches gradle test aus, um alle Szenarien auszuführen. Wie im unteren Abschnitt im Codebeispiel zu sehen ist, können Properties an Tests übergeben werden, um z. B. die parallele Ausführung von Szenarien zu erlauben oder um bestimmte Szenarien zu ignorieren.

Für Leute mit Zeitdruck haben wir unter https://github.com/cronn-de/cucumber-junit5-example ein Beispiel hochgeladen.