Execution Listener

As you might remember (or might look up in the chapter How does it work?), tapir builds an execution plan based on the test suites, cases, steps and various annotations. The execution plan is executed by the ExecutionPlanExecutor which itself notifies the execution listeners. In this chapter we take a more detailed look at this and show you how to hook into the execution of the test plan. The execution listeners are part of tapir’s execution module.

Dependency

<dependency>
    <groupId>de.bmiag.tapir</groupId>
    <artifactId>tapir-execution</artifactId>
</dependency>

Custom Execution Listener

In order to get notified during the test execution, you have to implement the interface ExecutionListener and make sure that Spring is aware of your component.

Hint
A more convenient base might be the AbstractExecutionListener. The interface ExecutionListener has 15 methods which you would have to implement, although you probably need only a few of them. The AbstractExecutionListener implements all those methods with an empty method body. By inheriting from this class, you only have to override the methods you really need. We will therefore use the AbstractExecutionListener from now on.

A minimal execution listener would look as following.

MyExecutionListener.xtend

@Component
class MyExecutionListener extends AbstractExecutionListener {
}

At the moment the listener has no real function. So let us implement one of the methods to be notified once a test step is executed.

MyExecutionListener.xtend

@Component
class MyExecutionListener extends AbstractExecutionListener {

    override stepStarted(TestStep testStep) {
        println("stepStarted: " + testStep.name)
    }
}

Once in the Spring context (the Component annotation takes care of that), the listener is already notified during the test execution and prints the name of all started test steps.

Order

As mentioned in the chapter How does it work?, there are already various listeners per default active during the test execution. There is one listener responsible for making screenshots in case that the execution failed, one listener is responsible for writing the Allure report files and so on. If you want to hook into the test plan execution, you should keep an eye on the order of the listeners. By using Spring’s @Order annotation, the listeners are notified in a deterministic order. In the following table you can find the listeners within tapir.

Execution Listener Order Description
StepStartedStateUpdater -10000 Sets the current step of the ExecutionState.
JUnitExecutionListener -7000 Fulfils the reporting API for JUnit.
HtmlPageCaptureListener -4000 Saves the static HTML content in case a step fails. If the current web driver allows JavaScript, the dynamic HTML content is saved as well.
LoggingExecutionListener 1500 Logs each event based on the logging configuration.
ScreenshotListener 4000 Makes a screenshot in case a step fails.
AllureExecutionListener 7000 Fulfils the reporting API for Allure and attaches all screenshots and saved HTML contents.
StepFinishedStateUpdater 10000 Resets the ExecutionState.

Your own execution listeners should usually have an order between -10000 and 10000. The precise order depends on the task you are performing. If, for instance, you want to make screenshots each time a step is started or succeeded (and not only if the step failed), you would have to use an order between -10000 and 7000 - because you have to provide the screenshot before the AllureExecutionListener starts to assemble the report files.

MyScreenshotListener.xtend

@Component
@Order(0)
class MyScreenshotListener extends AbstractExecutionListener {

    @Autowired
    ScreenshotService screenshotService

    override stepStarted(TestStep testStep) {
        val name = '''«testStep.parentTestClass.name».«testStep.name»-Screenshot-Before'''
        screenshotService.takeScreenshot(name)
    }

    override stepSucceeded(TestStep testStep) {
        val name = '''«testStep.parentTestClass.name».«testStep.name»-Screenshot-After'''
        screenshotService.takeScreenshot(name)
    }
}