Mihai Surdeanu

Create a simple Cucumber plugin

This article advice you on how a custom Cucumber plugin can be created to intercept test cases results and to publish them to a 3rd party entity. Since version 4.0.0, Cucumber allows custom plugins to be created and added to your internal test runners.

To create a new custom plugin, you will have to create a new Java class which will have to implement interface Plugin and EventListener or ConcurrentEventListener. Last two interfaces (EventListener and ConcurrentEventListener) defines just one method that needs to be implemented: setEventPublisher. This method is useful for providing a way to specify on which type of events you want to listen. In addition, there is a small difference between them. Most of the time, its recommended to implement ConcurrentEventListener because this method its thread safe and you don’t have to worry about the synchronization of multiple data structures, if you prefer to run Cucumer in a parallel environment.

Having all this in mind, let’s take a look together over one simple example of custom plugin:

package ro.mihaisurdeanu;

import io.cucumber.plugin.ConcurrentEventListener;
import io.cucumber.plugin.Plugin;
import io.cucumber.plugin.event.EventPublisher;
import io.cucumber.plugin.event.TestCaseFinished;
import io.cucumber.plugin.event.TestCaseStarted;

import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;

public class PublisherPlugin implements Plugin, ConcurrentEventListener {

    private static final Map<String, Instant> testCasesMap = new HashMap<>();

    public void setEventPublisher(EventPublisher eventPublisher) {
        eventPublisher.registerHandlerFor(TestCaseStarted.class, this::handleTestCaseStarted);
        eventPublisher.registerHandlerFor(TestCaseFinished.class, this::handleTestCaseFinished);
    }

    private void handleTestCaseStarted(TestCaseStarted testCaseStarted) {
        testCasesMap.put(testCaseStarted.getTestCase().getId().toString(), testCaseStarted.getInstant());
    }

    private void handleTestCaseFinished(TestCaseFinished testCaseFinished) {
        Instant testCaseStartedAt = testCasesMap.remove(testCaseFinished.getTestCase().getId().toString());
        if (testCaseStartedAt == null) {
            return;
        }

        System.out.println("Test name: " + testCaseFinished.getTestCase().getName());
        System.out.println("Test duration (in millis): " + Duration.between(testCaseStartedAt, testCaseFinished.getInstant()).toMillis());
        System.out.println("Test status: " + testCaseFinished.getResult().getStatus().toString());
    }

}

In my case, I’m listening for just two type of events: TestCaseStarted and TestCaseFinished. There are various event types:

  • TestRunStarted – Event sent when test run in started.
  • TestCaseStarted – Event sent before test scenario execution.
  • TestStepStarted – Event sent before step execution.
  • TestStepFinished – Event sent after step execution.
  • TestCaseFinished – Event sent after test scenario execution.
  • TestRunFinished – Event sent when test run in finished.

At this moment, there is just one remaining step: to link our custom plugin with a set of runners. In my case, I defined just one runner for all the tests:

package ro.mihaisurdeanu;

import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(features = {"src/test/resources/features/"},
        plugin = {"ro.mihaisurdeanu.PublisherPlugin"})
public class AllTestsRunner {
}

This is it! I forgot to say, the implementation provided is available for latest version of Cucumber: 6.8.0. 🙂

Happy coding!

Mihai

Pasionat de IT. Pasionat de viață. Pasionat de tot ceea ce înseamnă a face o viață mai bună, plină de înțelegere, ajutor reciproc și iubire de aproape.

Adaugă comentariu

Arhive

Arhiva personală