JUnit Listeners with All Details and Examples!

In this post, we will learn how to use JUnit Listeners together. If you want to do some operations when your tests are started, passed, finished, failed, or skipped/ignored, you can use Listeners. Both JUnit and TestNG provide us Listeners, and in this article, I will explain how to add JUnit 4 Listeners. 

Listeners in the test automation listen and handle events while your automation code is running. We can add a listener by creating a custom Runner. Then, we can use this custom runner in our test class with @RunWith annotation.

First, let’s write our Listener class by extending JUnit’s RunListener class and overriding its methods. I called our listener class as MyExecutionListener.

JUnit Listeners Examples

MyExecutionListener

package junit4listeners;

import java.util.Date;
import org.junit.Ignore;
import org.junit.runner.Description;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;

public class MyExecutionListener extends RunListener {

    //Start and End time of the tests
    long startTime;
    long endTime;

    @Override
    public void testRunStarted(Description description) {
        //Start time of the tests
        startTime = new Date().getTime();
        //Print the number of tests before the all tests execution.
        System.out.println("Tests started! Number of Test case: " + description.testCount() + "\n");
    }

    @Override
    public void testRunFinished(Result result) throws Exception {
        //End time of the tests
        endTime = new Date().getTime();
        //Print the below lines when all tests are finished.
        System.out.println("Tests finished! Number of test case: " + result.getRunCount());
        long elapsedSeconds = (endTime - startTime) / 1000;
        System.out.println("Elapsed time of tests execution: " + elapsedSeconds + " seconds");
    }

    @Override
    public void testStarted(Description description) {
        //Write the test name when it is started.
        System.out.println(description.getMethodName() + " test is starting...");
    }

    @Override
    public void testFinished(Description description) {
        //Write the test name when it is finished.
        System.out.println(description.getMethodName() + " test is finished...\n");
    }

    @Override
    public void testFailure(Failure failure) {
        //Write the test name when it is failed.
        System.out.println(failure.getDescription().getMethodName() + " test FAILED!!!");
    }

    @Override
    public void testIgnored(Description description) throws Exception {
        super.testIgnored(description);
        Ignore ignore = description.getAnnotation(Ignore.class);
        String ignoreMessage = String.format(
            "@Ignore test method '%s()': '%s'",
            description.getMethodName(), ignore.value());
        System.out.println(ignoreMessage + "\n");
    }
}

Second, we need to add the listener to the test execution. We can add the listener by calling the RunNotifier.addListener() method. This will register our JUnit Listener. I faced a problem when I tried to use RunListener class’s testRunStarted method. Then, I found a solution that triggers this method after registering the listener in the run method. I wrote a comment about this solution below code.

MyTestRunner

package junit4listeners;

import org.junit.AssumptionViolatedException;
import org.junit.internal.runners.model.EachTestNotifier;
import org.junit.runner.notification.RunNotifier;
import org.junit.runner.notification.StoppedByUserException;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;

public class MyTestRunner extends BlockJUnit4ClassRunner {

    public MyTestRunner(Class<?> clazz) throws InitializationError {
        super(clazz);
    }

    @Override
    public void run(RunNotifier notifier) {
        System.out.println("Executing run()");
        //Add Listener. This will register our JUnit Listener.
        notifier.addListener(new MyExecutionListener());

        //Get each test notifier
        EachTestNotifier testNotifier = new EachTestNotifier(notifier,
            getDescription());
        try {
            //In order capture testRunStarted method
            //at the very beginning of the test run, we will add below code.
            //Invoke here the run testRunStarted() method
            notifier.fireTestRunStarted(getDescription());
            Statement statement = classBlock(notifier);
            statement.evaluate();
        }
        catch (AssumptionViolatedException e) {
            testNotifier.fireTestIgnored();
        }
        catch (StoppedByUserException e) {
            throw e;
        }
        catch (Throwable e) {
            testNotifier.addFailure(e);
        }
    }
}

Third, we need to write a test to verify our listener’s functionality. We register our custom runner with @RunWith(MyTestRunner.class) annotation and for alphabetic test execution, we add @FixMethodOrder(MethodSorters.NAME_ASCENDING) annotation.

Extra: Also, you can combine listeners with the custom Retry Rule described in another JUnit article. Thus, when your test fails, it will rerun the failed test according to the given retry count.

ListenerExampleTest

package junit4listeners;

import io.github.bonigarcia.wdm.WebDriverManager;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;

@RunWith(MyTestRunner.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ListenerExampleTest {

    static WebDriver driver;
    final private static String URL = "https://www.swtestacademy.com";

    @BeforeClass
    public static void setupTest(){
        WebDriverManager.chromedriver().setup();
        ChromeOptions options = new ChromeOptions();
        options.setHeadless(true);
        driver = new ChromeDriver(options);
        driver.get(URL);
    }

    //Passed
    @Test
    public void T01_PassTest() {
        assertThat(driver.getTitle(), is("Software Test Academy"));
    }

    //Failed
    @Test
    public void T02_FailTest() {
        assertEquals("Title is wrong!!!", "WRONG TITLE", driver.getTitle());
    }

    //Ignore/Skip
    @Ignore
    public void T03_IgnoreTest() {
        assertThat(driver.getTitle(), is("Software Test Academy"));
    }

    //Throw Exception
    @Test
    public void T04_ExceptionTest() {
        throw new RuntimeException();
    }
}

Test Results

junit listeners

GitHub Project

https://github.com/swtestacademy/selenium-examples/tree/main/src/test/java/junit4listeners

Thanks,
Onur Baskirt

4 thoughts on “JUnit Listeners with All Details and Examples!”

  1. Thank you for your response.Hope you get well soon.
    I tried the approach mentioned above but the Result and failure class does not have any get.Instance() method.

    My problem statement is as :
    I have a selenium project that has junit test cases. I have another project that has junitlisteners implementation.
    I have used the dependency of my second project in 1st to integrate them.
    When I try to run the test cases using mytestrunner ,I am unable to find a way to send my driver.

    Whereas when I had used the 2nd project in integration to other project that has TestNG framework I was able to achieve it using ITestContext and getInstanceName.

    public void onTestFailure(ITestResult result) {
    ITestContext context = result.getTestContext();
    driver = (WebDriver) context.getAttribute(“driver”);
    if(driver != null) {
    this.takeScreenShot(result.getInstanceName());
    } else{
    System.out.println(“There is no driver.”);
    }
    }

    I am trying to implement something similar to this in Junitlistener implemented method.

    Any guidance would be really appreciated.

    Reply
    • 1) In your testclass or basetest class where u created the driver write a get method in this way:
      public WebDriver getDriver() {
      return driver;
      }

      2) In listener class, in testFailure method write below statements.

      @Override
      public void testFailure(Failure failure) throws Exception {
      Object testClass = failure.getDescription().getTestClass();
      WebDriver driver = ((Write your test class Name or write your base test class name where you created webdriver instance) testClass).getDriver();

      //Now use driver as you wish

      //Write the test name when it is failed.
      System.out.println(failure.getDescription().getMethodName() + ” test FAILED!!!”);
      }

      I hope this helps.

      Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.