Hero Image
- 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 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, it's recommended to implement ConcurrentEventListener because this method is thread safe and you don’t have to worry about the synchronization of multiple data structures, if you prefer to run Cucumber in a multithreaded 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!

Other Related Posts:

Microbenchmarking with Java

Microbenchmarking with Java

Hello guys,

Today, we are going to continue the series of articles about Java ecosystem. More specific, today, we are going to talk about JMH (Java Microbenchmark Harness).

2nd Apr 2022 - Mihai Surdeanu