Introduction
What to do when existing Cucumber plugins do not meet ones requirements? Cucumber allows custom plugins to be created and added to the runner. The steps for accomplishing this is detailed in this article. This is applicable to Cucumber version 4.0.0 and greater. A brief explanation of how to deal with previous versions can be found at the end.
Plugin Creation
The custom plugin needs to implement one of cucumber.api.event.EventListener or cucumber.api.event.ConcurrentEventListener interface. Else one can also implement the cucumber.api.StepDefinitionReporter and/or cucumber.api.SummaryPrinter. These in turn extend the cucumber.api.Plugin interface.
This custom plugin needs to have a no-argument constructor if passing argument is optional or none are required. To pass an argument a single-argument constructor needs to be added. The argument passed to the single-argument constructor needs to be one of these classes – java.lang.String, java.lang.Appendable, java.net.URI, java.net.URL or java.io.File. There can be only one single-argument constructor.
public class CustomPlugin implements EventListener { public CustomPlugin() { } public CustomPlugin(File file) { } }
The most useful interfaces to implement are the EventListener or ConcurrentEventListener which have callback methods that need to be implemented in the custom plugin class for execution events. The CustomPlugin class above has both constructors which allows for the case of optional argument. Depending on ones requirements both or only one constructor may be sufficient.
Plugin Addition
The custom plugin needs to be mentioned, along with the fully qualified class name, in plugins option of the CucumberOptions annotation on the runner class. There can be multiple plugins mentioned in this, including custom plugins. Below is an example with no arguments, other options are not shown.
@CucumberOptions(plugin = {"formatter.CustomPlugin"})
Below is an example in which an argument is mentioned. This argument will be passed onto the single argument constructor. The ‘:’ delimiter is used to separate the class and the argument.
@CucumberOptions(plugin = {"formatter.CustomPlugin:run.log"})
This is all that is required to implementing a custom plugin.
EventListener & ConcurrentEventListener
When the custom plugin needs to respond to cucumber events then these two interfaces are the best choice. These interfaces have one method setEventPublisher which takes a cucumber.api.event.EventPublisher as an argument which need to be implemented. In this method an event handler is registered for each event that one is interested in listening to.
The example below uses Java8 method references for all the event handlers, which may not be required in all cases. Otherwise one can create an inline object for the cucumber.api.event.EventHandler interface and pass the event to the receive() method.
public void setEventPublisher(EventPublisher publisher) { publisher.registerHandlerFor(TestRunStarted.class, this::handleRunStarted); //Add other handlers for events as required. }
The various event types for which handlers can be added are mentioned below. More details about these events can be found here.
- TestRunStarted – Event sent when test run in started. Contains the timestamp of the test run start.
- TestSourceRead – Event sent when feature file is read. Contains the location of the feature file and its contents.
- TestCaseStarted – Event sent before scenario execution. Contains the scenario details like uri, line, steps, tags etc.
- TestStepStarted – Event sent before step execution. Contains the step details.
- TestStepFinished – Event sent after step execution. Contains the step details and result of the step.
- TestCaseFinished – Event sent after scenario execution. Contains the scenario details and test case result.
- TestRunFinished – Event sent when test run in finished. Contains the timestamp of the test run end.
- EmbedEvent – Event sent when scenario.embed is called inside a hook. Contains the byte array and mime type.
- WriteEvent – Event sent when scenario.write is called inside a hook. Contains the text.
- SnippetsSuggestedEvent – Event sent when step cannot be matched to a step definition. Contains details like uri, locations etc.
ConcurrentEventListener is used when one requires events to be relayed in real time. When this is used in case of parallel execution, the plugin needs to take care of management of the different threads. In case of EventListener the events are emitted after the completion of the test run. The events are sorted in canonical order (cucumber.api.event.CanonicalEventOrder) and then emitted to the plugin.
Previous versions
The difference between the versions is only what interfaces need to be implemented by the custom plugin class. The process of adding the custom plugin to the cucumber.api.CucumberOptions annotation remains the same.
For cucumber version upto 1.2.5, the custom plugin needs to implement the gherkin.formatter.Formatter and/or gherkin.formatter.Reporter interface. The Formatter interface deals mainly with events relating to the feature file while the Reporter interface contains the results of the execution. These two are the most useful interfaces to implement. One can also use the cucumber.api.StepDefinitionReporter and/or cucumber.api.SummaryPrinter interfaces.
For cucumber version from 2.0.0 onwards till 3.0.2, the custom plugin needs to implement the cucumber.api.formatter.Formatter marker interface, which in turn extends the cucumber.api.event.EventListener interface. Thus one will need to implement the setEventPublisher method similar to above.
Hi, how to attach screenshot using custom plugin?
use the EmbedEvent. write the screenshot code in handleEmbed. Refer to this – https://github.com/grasshopper7/extentreports-cucumber6-adapter/blob/33f2c9a06314fa7d8b963549008c57149f243ad7/extentreports-cucumber6-adapter/src/main/java/com/aventstack/extentreports/cucumber/adapter/ExtentCucumberAdapter.java#L224
private EventHandler embedEventhandler = new EventHandler () {
@Override
public void receive(EmbedEvent event) {
handleEmbed(event);
}
};
Thanks for the quick reply. As I understand the given solution will work with Extent Report. Please correct me, if I’m not Mistaken.
However in my case for reporting I am using Allure and maven-cucumber-reporting. Can you please provide any reference for same?
The EmbedEvent is a cucumber plugin event. It has got nothing to do with ExtentReports. The screenshot byte data will be available in event.getData().
I spent a great deal of time to locate something similar to this
Hi there! Such a great short article, thanks!