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:"; @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
GitHub Project
https://github.com/swtestacademy/selenium-examples/tree/main/src/test/java/junit4listeners
Thanks,
Onur Baskirt

Onur Baskirt is a Software Engineering Leader with international experience in world-class companies. Now, he is a Software Engineering Lead at Emirates Airlines in Dubai.
If I have to send my webdriver to the junittestlistener , how do I do that?
https://stackoverflow.com/questions/12429793/can-selenium-take-a-screenshot-on-test-failure-with-junit
I found this but I dont know it will help your question. I need to think that as well.
In TestNG we did that in below way:
//Get driver from BaseTest and assign to local webdriver variable.
Object testClass = iTestResult.getInstance();
WebDriver webDriver = ((BaseTest) testClass).getDriver();
Try the same approach in JUnit.
Object testClass = Result.getInstance();
Webdriver webdriver = ((Your Test Class or Base TEst Class HERE) testClass).getDriver();
In your test class write a getDRiver method which returns driver. I am very sick now. If I will be better, I will try to do that as well. I hope this approach will work.
TestNG Code is here: https:/extentreports-testng/
Look at TestListener’s onTestFailure method.
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.
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.