,

Java Projekt mit JUnit5, Cucumber7 und Allure

JUnit5 - Jupiter Logo

Das Internet vergisst nicht. Aber Versionierung ist nicht seine Stärke. Beim Umstieg auf neue Versionen von verwendeten Modulen ist es zum Teil schwierig, die richtige Beschreibung zu finden. Ich habe jüngst ein Projekt von einem recht konservativen Versionsstand auf einen aktuelleren Stand gebracht, dabei wohl auch einige Versionen übersprungen. Aber die für die neue Version richtige Konfiguration zu bekommen, war schon etwas Detektivarbeit. Um euch diese Suche hoffentlich etwas zu erleichtern, schreibe ich hier eine Konfiguration für ein kleines Beispielprojekt zusammen.

Ein Java Projekt mit JUnit5, Cucumber und Allure konfigurieren

Dieses Beispielprojekt soll Gherkin-Beschreibungen mit Hilfe der Cucumber-Engine in ausführbare Tests übersetzen. Dazu verwenden wir JUnit5 als Testframework für Java, und möchten die Ergebnisse mit Hilfe von Allure in Reports aufbereiten. Um mit einer API zu arbeiten, verwenden wir REST-Assured.

Paket Version GroupID
Cucumber 7.12.0 io.cucumber
JUnit5 – Jupiter 5.9.3 org.junit
Allure 2.24.0 io.qameta.allure
rest-assured 5.3.2 io.rest-assured

 Start mit einem neuen Projekt

Ein frisch erstelltes Maven-Projekt ist unser Ausgangspunkt. In meinem Fall habe ich es mit der IDE IntelliJ Community Edition erstellt. Wer sich aber eingehender mit Maven auseinandersetzen möchte, kann sich gerne die Maven Archetypes ansehen. Letztere können dabei helfen, ein Projekt mit den passenden Abhängigkeiten nach einem vorgegebenen Schema anzulegen. Für unser kleines Projekt reicht aber das leere Projekt. Wir gehen die Abhängigkeiten Schritt für Schritt durch.

<?xml version="1.0" encoding="UTF-8"?>
    <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>org.example</groupId>
        <artifactId>thBerryShopExample</artifactId>
        <version>1.0-SNAPSHOT</version>
        <properties>
            <maven.compiler.source>17</maven.compiler.source>
            <maven.compiler.target>17</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    </project>

CucumberCucumber Open Logo

Wir starten mit dieser übersichtlichen pom.xml. Als erstes wollen wir Cucumber einbinden, zum einen die JUnit Platform Engine und als zweites die cucumber-java Bibliothek. Ersteres verbindet den Cucumber-Runner mit unserem Testframework, während das zweite die Sprachsyntax von Gherkin innerhalb unseres Java-Codes verfügbar macht. Der schnellste Weg besteht darin, einfach die im MVN Repository angegebene dependency in den neu zu erstellenden Tag dependencies in unsere POM aufzunehmen.

    </properties>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-java -->
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>7.12.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-junit-platform-engine -->
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-junit-platform-engine</artifactId>
            <version>7.12.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

Etwa so. Dabei fällt schon auf, dass wir zweimal die gleiche Version geschrieben haben. Das muss auch so sein, damit diese auch zusammenarbeiten können. Maven bietet uns hier aber die Möglichkeit zur Vereinfachung. Wir können mit Hilfe der Dependency-Management-Methode die Version zentral festlegen, und haben später nur eine Stelle, die wir anpassen müssen. In diesem kleinen Projekt ist das nicht so wichtig, aber mit steigenden Abhängigkeiten hilft es, den Überblick zu behalten. Eine einfache Alternative wäre, in den Properties eine Variable hinzuzufügen: <cucumber.version>7.12.0</cucumber.version>. Nun können wir in den Modulen bei version ${cucumber.version} eintragen, und haben eine zentrale Stelle zum Umstellen. Ich bevorzuge den dependencyManagment Eintrag, aber die Properties-Variante ist in einigen Situationen hilfreich.

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.cucumber</groupId>
                <artifactId>cucumber-bom</artifactId>
                <version>7.12.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

Mit dieser “bill of materials”, kurz bom, werden alle Abhängigkeiten vorgemerkt mit der Version, aber noch nicht importiert. Anschließend können die dependencies jetzt ohne Version angegeben werden, unsere pom.xml erweitern wir jetzt also um diesen oben genannten Bereich des dependencyManagment und den folgenden Block.

    <dependencies>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-java</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-junit-platform-engine</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

JUnit5 importierenJUnit5 - Jupiter Logo

Als nächstes benötigen wir die junit-platform-suite und die Bibliothek junit-jupiter. Wie bei den Cucumber-Paketen sollen diese in der gleichen Version verwendet werden, daher erweitern wir die entsprechenden Stellen.

    <dependencyManagement>
        <dependencies>
            ...
            <dependency>
                <groupId>org.junit</groupId>
                <artifactId>junit-bom</artifactId>
                <version>5.9.3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        ...
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-suite</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

Erste Gherkin-Statements

An dieser Stelle haben wir die Imports für einen einfachen Funktionstest fertig. Da wir mit einem leeren Projekt gestartet haben, müssen wir noch ein paar Dateien erstellen. Zum einen unter src/test/resources eine *.feature Datei. Und in src/test/java die Datei RunCucumberTest.java mit folgendem Inhalt:

RunCucumberTest.java

import org.junit.platform.suite.api.IncludeEngines;
import org.junit.platform.suite.api.SelectClasspathResource;
import org.junit.platform.suite.api.Suite;

@Suite
@IncludeEngines("cucumber")
@SelectClasspathResource(".")
public class RunCucumberTest {
}

example.feature

    Feature: Get Cucumber to work

    Scenario: Run a simple Cucumber Job
      Given this works as intended  
      When the test runner is called    
      Then this lines are executed

In der Java-Datei wird die Klasse RunCucumberTest über die Deklarationen als Testsuite für JUnit bekannt gemacht, dass es mit der cucumber-engine bearbeitet werden soll. Und über das letzte Element können Unterordner referenziert werden, wo im resources Bereich die *.feature Dateien zu suchen sind. Eine IDE wie IntelliJ wird die Given-, When- und Then-Sätze markieren, da hierfür die Java-Implementierung noch fehlt. Spätestens wenn ihr mvn test ausführt, wird mitgeteilt, dass es zu den Sätzen keine Entsprechung gibt. Legen wir diese also an im Ordner src/test/java als MyStepdefs.java.

    import io.cucumber.java.en.Given;
    import io.cucumber.java.en.Then;
    import io.cucumber.java.en.When;
    public class MyStepdefs {
        @Given("this works as intended")
        public void thisWorksAsIntended() {
        }
        @When("the test runner is called")
        public void theTestRunnerIsCalled() {
        }
        @Then("this lines are executed")
        public void thisLinesAreExecuted() {
        }
    }

Mit dieser Datei angelegt, kann die cucumber-engine nun unsere Gherkin-Sätze auf Java-Code übersetzen. Damit haben wir unseren ersten Teil, das Ausführen von Tests mittels Cucumber, erreicht. Dann wollen wir hier den Allure Report einbringen.

Einbindung Allure ReportLogo von Allure Report

Um Allure zu nutzen, müssen wir dieses in unserer Testumgebung bekannt machen. Dafür erweitern wir unsere dependencies in der pom.xml um folgende Einträge:

    <dependency>
        <groupId>io.qameta.allure</groupId>
        <artifactId>allure-junit5</artifactId>
        <version>2.24.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.qameta.allure</groupId>
        <artifactId>allure-cucumber7-jvm</artifactId>
        <version>2.24.0</version>
        <scope>test</scope>
    </dependency>

Führen wir jetzt mvn test in unserem Projekt aus, werden wir mit folgendem Error belohnt:

[ERROR] TestEngine with ID ‚junit-platform-suite‘ failed to discover tests

Hintergrund ist, dass wir jetzt zwar Allure für den Report verwenden wollen, aber der weiß noch nichts von unseren Tests. Hier haben sich die Wege öfter geändert, daher stößt man noch oft auf die alten Varianten, die hier aber nicht mehr funktionieren. Die neue Notation geht über @ConfigurationParameter, die wir unserer Klasse RunCucumberTest anhängen müssen.

import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME;
import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME;

@Suite
@IncludeEngines("cucumber")
@SelectClasspathResource(".")
@ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty")
@ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "io.qameta.allure.cucumber7jvm.AllureCucumber7Jvm")
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = ".")
public class RunCucumberTest {
}

Wenn eure Imports auch korrekt aufgelöst sind, werden diese Zeilen den Allure darüber informieren, welche Tests er reporten soll. Jetzt sollte ein entsprechender Aufruf von mvn test erfolgreich sein, und der Ordner allure-results ist aufgetaucht. Wenn ihr jetzt noch mit allure generate report den Report erzeugt, könnt ihr mit allure open einen Bericht anzeigen lassen.

Nur bestimmte Tags ausführen

Mit dem Aufruf von mvn test werden alle Szenarien, die in gefunden Feature-Dateien wurden, ausgeführt. In vielen Fällen wird man das etwas genauer steuern wollen, daher kann man Szenarien oder auch ganze Features mit @Tags versehen, bspw. alle Standardszenarien mit @regression versehen, oder wenn man an neuer Logik arbeitet, ein @inDev vergeben. Anschließend kann man jetzt beim Aufruf einschränken, indem man das oder die gewünschten Tags angibt.

  mvn test -Dcucumber.filter.tags="--tags @tagname"
  mvn test -Dcucumber.filter.tags="--tags @regression and not @inDev"

Die zweite Zeile demonstriert, wie man die Tags logisch verknüpfen kann. In diesem Fall sollen alle Regressionstests ausgeführt werden die nicht das Tag @inDev anhängen haben. Hier gibt es noch einen kleinen Fallstrick für Windows Nutzer. Diese müssen in dem Aufruf das -D mit einem Akzent (`) maskieren:

mvn test `-Dcucumber.filter.tags="--tags @tagname"

In älteren Version von Cucumber wird mit den cucumber.options gearbeitet. Die Aufrufe hierfür findet ihr nachfolgend.

mvn test -Dcucumber.options="--tags @tagname"

In der Windows-Powershell muss das -D ebenfalls mit einem Akzent (`) maskiert werden:

mvn test `-Dcucumber.options="--tags @tagname"

Fazit

Nach dieser kleinen Lektüre solltet ihr ein Java Projekt mit JUnit5, Cucumber und Allure fertig konfiguriert haben. Mit den neuen Versionen kamen einige neue Notationen einher um die Abhängigkeiten korrekt zu konfigurieren. Ich hoffe dieses Beispiel hat euch geholfen euer Projekt richtig einzustellen. Wenn euch alternative Wege bekannt sind, ihr Fragen oder Anregungen zu dem Artikel habt, lasst es mich in den Kommentaren wissen.

Hier geht es zu den Sourcen auf GitHub.com

0 Kommentare

Hinterlasse einen Kommentar

An der Diskussion beteiligen?
Hinterlasse uns deinen Kommentar!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert