TestNG Listeners – Selenium WebDriver

TestNG Listeners – Selenium WebDriver: In this post, we will discuss the listeners in TestNG as we have discussed many important features of implementations of TestNG.

This feature is also the most crucial feature of TestNG. Before discussing Listeners, let us start with What a listener is & different types of Listeners.

What is the Listener?

The listener is defined as an interface that modifies the default behavior. As the name suggests, listeners “Listen” to the event and keep track of the test execution while performing some action by the Selenium WebDriver at every stage and Behave accordingly. We can use this by implementing the Listener Interface.

We can also customize the test logs and report according to your project requirements, which helps set the proper message in the execution report so that anyone can easily understand by going through it.

There are different types of events present, and with the help of those, we can generate our own events when a test case fails/passes/ or is skipped.

What Is an Event?

An event refers to any action we perform using Selenium WebDriver, like clicking, typing, navigating, etc. These all are termed as Events.

Different Types Of Listeners

There are two main listeners available:

WebDriver Listeners
TestNG Listeners

We have discussed WebDriver listener briefly in a separate post, and You can read that by following this link. Now, we are going to discuss the TestNg Listeners briefly in Detail:

We can use the TestNg Listeners in Selenium WebDriver at Two Levels:

  • Class Level: If you want to apply for all the @Test methods of a class, then you can define it at the class level.
  • Suite Level: If you want to apply the listeners to all classes of a specific suite, you can use them at the suite level.

Types Of TestNG Listeners

In TestNG, We Can find many TestNG Listeners. Still, we will discuss those listeners frequently used by most automation testers while writing the Selenium WebDriver automation script.

So we are going to cover below testNG Listeners in our Post; those are:

ITestListener
IInvokedMethodListener
IReporter
IExecutionListener
IMethodInterceptor
IAnnotationTransformer
ISuiteListener
IConfigurable
IHookable
IConfigurationListener

Now let us start one by one and understand the use of the above listeners and how we can implement them in our script with some examples.

ITestListener

This is one of the most used TestNG Listeners in Selenium WebDriver. To use this listener, you need to implement it in your Java class and override all methods present inside this interface.

With the help of this listener in Selenium WebDriver, we can change the default behavior of the test by adding different methods to the existing methods of that listener interface.

The following are the methods that are present inside the Interface:

  • onStart: This method is executed each time before executing any test method.
  • onFinish: This method is executed after the execution of all the test methods.
  • onTestStart:
  • onTestSkipped:
  • onTestSuccess:
  • onTestFailure:
  • onTestFailedButWithinSuccessPercentage:

When using ITestListener and want to get the results’ details, you must pass the following parameters to those methods: the “ITestResult” interface and the instance “Result.”

Example:

public void onTestStart(ITestResult result) 
{
   System.out.println("New Test Started" +result.getName());
}

Note: If you want to trace all the exceptions using ItestResult, don’t use the try/catch handling in the script.

To get all the information about the test run, you must pass the “ITestContext” interface as a parameter with its instance “context.”

public void onStart(ITestContext context)
{
   System.out.println("This is onStart method" +context.getOutputDirectory());
}

Take a simple example and implement the Listener at the Class level. The logs will be generated at a console to help us understand which test passed, failed, and skipped.

Steps to Follow:

  • Create a Class and implement all the methods of the ITestListener interface.
  • Create another class that has your test cases and add the listener, which we created on that.

Listener Implemented Class:

package com.softwaretestingo.testng.listeners;
import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;
public class ListenerImplementedClass implements ITestListener 
{
	public void onTestStart(ITestResult result) 
	{
		System.out.println("New Test Started" + result.getName());
	}

	public void onTestSuccess(ITestResult result) 
	{
		System.out.println("Test Successfully Finished" + result.getName());
	}

	public void onTestFailure(ITestResult result) 
	{
		System.out.println("Test Failed" + result.getName());
	}

	public void onTestSkipped(ITestResult result) 
	{
		System.out.println("Test Skipped" + result.getName());
	}

	public void onTestFailedButWithinSuccessPercentage(ITestResult result) 
	{
		System.out.println("Test Failed but within success percentage" + result.getName());
	}

	public void onStart(ITestContext context) 
	{
		System.out.println("This is onStart method" + context.getOutputDirectory());
	}

	public void onFinish(ITestContext context) 
	{
		System.out.println("This is onFinish method" + context.getPassedTests());
		System.out.println("This is onFinish method" + context.getFailedTests());
	}
}

We need to create another class where we can write our test cases, which have been written inside the Test Methods, and add the previously created listener above the class name to implement it at the class level.

Syntax:

@Listeners(PackageName.ClassName.class)

Test Class:

package com.softwaretestingo.testng.listeners;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.SkipException;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
//@Listeners(com.softwaretestingo.testng.listeners.ListenerImplementedClass.class)
public class TestNGListenersTest 
{
	@Test //Passing Test
	public void sampleTest1() throws InterruptedException
	{
		WebDriver driver = new ChromeDriver();
		driver.get("https://opencart.softwaretestingo.com/");
		driver.manage().window().maximize();
		driver.findElement(By.linkText("My Account")).click();
		Thread.sleep(2000);
		driver.findElement(By.linkText("Login")).click();
		Thread.sleep(2000);
		driver.findElement(By.xpath("(//button)[4]")).click();
		Thread.sleep(2000);
		driver.quit();
	}
	@Test //Failing Test
	public void sampleTest2() throws InterruptedException
	{
		System.out.println("Forcely Failed Test Method");
		Assert.assertTrue(false);
	}
	private int i = 0;
	//Test Failing But Within Success Percentage
	@Test(successPercentage = 60, invocationCount = 5) 
	public void sampleTest3() 
	{
		i++;
		System.out.println("Test Failed But Within Success Percentage Test Method, invocation count: " + i);
		if (i == 1 || i == 2)
		{
			System.out.println("sampleTest3 Failed");
			Assert.assertEquals(i, 6);
		}
	}
	//Skipping Test
	@Test 
	public void sampleTest4()
	{
		throw new SkipException("Forcely skipping the sampleTest4");
	}
}

Suppose there are multiple classes in your project, and you need to add TestNg Listener in all classes; then that’s a difficult job; in that case, you can define in Suite Level Like Below:

Testng.xml Sample Code

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
	<listeners>
		<listener class-name="com.softwaretestingo.testng.listeners.ListenerImplementedClass"></listener>
	</listeners>
	<test thread-count="5" name="Test">
		<classes>
			<class
				name="com.softwaretestingo.testng.listeners.TestNGListenersTest" />
		</classes>
	</test> <!--
	Test -->
</suite> <!--
Suite -->

IAnnotationTransformer

The IAnnotationTransformer interface has a method called “transform,” which is used to modify the TestNG behavior of the @Test test class method.

This transform method takes four parameters are those are:

  • Annotation: This annotation is used to read from the test class.
  • Test class: If this annotation is found in a class, it represents the same class.
  • testConstructor: If this annotation finds a constructor, it returns the same constructor.
  • testMethod: If this annotation is found on a method, this parameter represents the same method.

Note: At least one of the parameters will be non-null.

Listener Class:

package com.softwaretestingo.testng.listeners;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.testng.IAnnotationTransformer;
import org.testng.annotations.ITestAnnotation;
public class AnnotationTransformers implements IAnnotationTransformer 
{
	public boolean isTestRunning(ITestAnnotation ins)
	{
		if(ins.getAlwaysRun())
		{
			return true;
		}
		return false;
	}

	public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) 
	{
		if(isTestRunning(annotation))
		{
			annotation.setEnabled(false);
		}
	}
}

Test Class:

package com.softwaretestingo.testng.listeners;
import org.testng.annotations.Test;
public class AnnotationTransformerTests 
{
	@Test(alwaysRun=true)
	public void test1()
	{
		System.out.println("This is my first test whose behaviour would get changed while executing");
	}

	@Test
	public void test2()
	{
		System.out.println("This is my second test executing");
	}
}

TestNG.XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
	<listeners>
		<listener
			class-name="com.softwaretestingo.testng.listeners.AnnotationTransformers"></listener>
	</listeners>
	<test thread-count="5" name="Test">
		<classes>
			<class
				name="com.softwaretestingo.testng.listeners.AnnotationTransformerTests" />
		</classes>
	</test> <!--
	Test -->
</suite> <!--
Suite -->

Explanation:

If you saw the test class, we have mentioned the attribute alwaysRun=”true,” which means the test method would always run even if the parameters on which the method depends fail.

But by implementing the IAnnotationTransformer and Transformer method in the Listener class, we change the default behavior of the TestNG class. Because of that, only the test2 method was executed.

Output:

This is my second test executing

===============================================
Suite
Total tests run: 1, Passes: 1, Failures: 0, Skips: 0
===============================================

IInvokedMethodListener:

This interface allows us to perform some actions before and after executing a method.
This listener is executed for both configuration and test methods.
This interface has two methods, and that is:

  • beforeInvocation(): This method is invoked before every method.
  • afterInvocation(): This method is invoked after every method.

Listener Class:

InvokedMethodListeners.java(includes listeners implemented methods)

package com.softwaretestingo.testng.listeners;
import org.testng.IInvokedMethod;
import org.testng.IInvokedMethodListener;
import org.testng.ITestResult;
public class InvokedMethodListeners implements IInvokedMethodListener
{
	public void beforeInvocation(IInvokedMethod method, ITestResult testResult) 
	{
		System.out.println("Before Invocation of: " + method.getTestMethod().getMethodName() + "of Class:" + testResult.getTestClass());
	}

	public void afterInvocation(IInvokedMethod method, ITestResult testResult)
	{
		System.out.println("After Invocation of: " + method.getTestMethod().getMethodName() + "of Class:" + testResult.getTestClass());
	}
}

Test Class:

package com.softwaretestingo.testng.listeners;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@Listeners(value=InvokedMethodListeners.class)
public class InvokedMethodListenersTest 
{
	@Test
	public void test1()
	{
		System.out.println("My first test");
	}

	@Test
	public void test2()
	{
		System.out.println("My second test");
	}

	@BeforeClass
	public void setUp() 
	{
		System.out.println("Before Class method");
	}
	@AfterClass
	public void cleanUp() 
	{
		System.out.println("After Class method");
	}
}

Output:

Before Invocation of: setUpof Class:[TestClass name=class com.softwaretestingo.testng.listeners.InvokedMethodListenersTest]
Before Class method
After Invocation of: setUpof Class:[TestClass name=class com.softwaretestingo.testng.listeners.InvokedMethodListenersTest]
Before Invocation of: test1of Class:[TestClass name=class com.softwaretestingo.testng.listeners.InvokedMethodListenersTest]
My first test
After Invocation of: test1of Class:[TestClass name=class com.softwaretestingo.testng.listeners.InvokedMethodListenersTest]
Before Invocation of: test2of Class:[TestClass name=class com.softwaretestingo.testng.listeners.InvokedMethodListenersTest]
My second test
After Invocation of: test2of Class:[TestClass name=class com.softwaretestingo.testng.listeners.InvokedMethodListenersTest]
Before Invocation of: cleanUpof Class:[TestClass name=class com.softwaretestingo.testng.listeners.InvokedMethodListenersTest]
After Class method
After Invocation of: cleanUpof Class:[TestClass name=class com.softwaretestingo.testng.listeners.InvokedMethodListenersTest]
PASSED: com.softwaretestingo.testng.listeners.InvokedMethodListenersTest.test2
PASSED: com.softwaretestingo.testng.listeners.InvokedMethodListenersTest.test1

===============================================
    Default test
    Tests run: 2, Failures: 0, Skips: 0
===============================================


===============================================
Default suite
Total tests run: 2, Passes: 2, Failures: 0, Skips: 0
===============================================

ISuiteListener

This TestNG is implemented at the Suite level. Has two methods:

  • onStart: This method is executed before the test suite execution.
  • onFinish: This method is executed after the test execution.

This listener listens to the events before and after the suite’s execution. If the parent suite further contains child suites, then child suites are executed before running the parent suite.

Listener Class:

package com.softwaretestingo.testng.listeners;
import org.testng.ISuite;
import org.testng.ISuiteListener;
public class SuiteListeners implements ISuiteListener
{
	public void onStart(ISuite suite) 
	{
		System.out.println("Suite executed onStart" + suite.getName());
	}
	public void onFinish(ISuite suite) 
	{
		System.out.println("Suite executed onFinish" + suite.getName());
	}
}

Test1:

package com.softwaretestingo.testng.listeners;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;
public class SuiteListenersTests1 
{
	@BeforeSuite
	public void test1()
	{
		System.out.println("BeforeSuite method in Suite1");
	}

	@Test
	public void test2()
	{
		System.out.println("Main Test method 1");
	}

	@AfterSuite
	public void test3()
	{
		System.out.println("AfterSuite method in Suite1");
	}
}

Test2:

package com.softwaretestingo.testng.listeners;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;
public class SuiteListenersTests2 
{
	@BeforeSuite
	public void test1()
	{
		System.out.println("BeforeSuite method in Suite2");
	}

	@Test
	public void test2()
	{
		System.out.println("Main Test method2");
	}

	@AfterSuite
	public void test3()
	{
		System.out.println("AfterSuite method in Suite2");
	}
}

TestNG XML File

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
	<listeners>
		<listener
			class-name="com.softwaretestingo.testng.listeners.SuiteListeners"></listener>
	</listeners>
	<test thread-count="5" name="Test">
		<classes>
			<class
				name="com.softwaretestingo.testng.listeners.SuiteListenersTests2" />
			<class
				name="com.softwaretestingo.testng.listeners.SuiteListenersTests1" />
		</classes>
	</test> <!--
	Test -->
</suite> <!--
Suite -->

Output:

Suite executed onStartSuite
BeforeSuite method in Suite1
BeforeSuite method in Suite2
Main Test method2
Main Test method 1
AfterSuite method in Suite1
AfterSuite method in Suite2
Suite executed onFinishSuite

===============================================
Suite
Total tests run: 2, Passes: 2, Failures: 0, Skips: 0
===============================================

IReporter

This TestNG interface helps us customize the test report generated by the TestNG. This interface has one method called generateReport, which will execute after executing all the suites. We need to pass three parameters to the generateReport method.

  • xmlSuite: If you provide the list of suites of the testng.xml file, which must be executed.
  • Suites: This object represents various information like classes, packages, and test execution results, along with all the test methods. We can say that it will give us detailed information about the suite after the final execution.
  • outputDirectory: It has the output folder where the reports get generated.

Listener Class:

package com.softwaretestingo.testng.listeners;
import java.util.List;
import java.util.Map;
import org.testng.IReporter;
import org.testng.ISuite;
import org.testng.ISuiteResult;
import org.testng.ITestContext;
import org.testng.xml.XmlSuite;
public class ReporterListener implements IReporter
{
	@Override
	public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites,String outputDirectory) 
	{
		//Iterate over every suite assigned for execution
		for (ISuite suite : suites) 
		{
			String suiteName = suite.getName();
			Map<String, ISuiteResult> suiteResults = suite.getResults();
			for (ISuiteResult sr : suiteResults.values()) 
			{
				ITestContext tc = sr.getTestContext();
				System.out.println("Passed tests for suite '" + suiteName +	"' is:" + tc.getPassedTests().getAllResults().size());
				System.out.println("Failed tests for suite '" + suiteName +	"' is:" + tc.getFailedTests().getAllResults().size());
				System.out.println("Skipped tests for suite '" + suiteName +"' is:" + tc.getSkippedTests().getAllResults().size());
			}
		}
	}
}

Test Class:

package com.softwaretestingo.testng.listeners;
import org.testng.Assert;
import org.testng.annotations.Test;
public class ReporterListenerTest 
{
	@Test
	public void firstMethod() 
	{
		Assert.assertTrue(true);
	}

	@Test
	public void secondMethod() 
	{
		Assert.assertTrue(false);
	}

	@Test(dependsOnMethods = {"firstMethod"})
	public void thirdMethod() 
	{
		Assert.assertTrue(true);
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
	<listeners>
		<listener
			class-name="com.softwaretestingo.testng.listeners.ReporterListener"></listener>
	</listeners>
	<test thread-count="5" name="Test">
		<classes>
			<class
				name="com.softwaretestingo.testng.listeners.ReporterListenerTest" />
		</classes>
	</test> <!--
	Test -->
</suite> <!--
Suite -->

Output:

===============================================
Suite
Total tests run: 3, Passes: 2, Failures: 1, Skips: 0
===============================================

Passed tests for suite 'Suite' is:2
Failed tests for suite 'Suite' is:1
Skipped tests for suite 'Suite' is:0

Not Frequently Used In TestNG Listener:

We have now discussed a few more listeners not frequently used in the automation script.

IConfigurationListener

This interface is used when you want to perform some operation when the configuration method is passed, failed, or skipped. IConfigurationListener has three methods, and that is:

  • onConfigurationSuccess: This method will be executed when a configuration method is passed.
  • onConfigurationFailure: This method will be executed when a configuration method fails.
  • onConfigurationSkip: When a configuration method gets skipped at that time, this method will be executed.

IExecutionListener

This listener interface determines when the suite or test starts or finishes. It has two methods, and those are:

  • onExecutionStart: This method is executed before the test or suite starts running.
  • onExecutionFinish: This method is executed after the test or suite starts execution is completed.

Note: It is not possible for this listener to prevent the execution but only to create events in some way. Also, you can provide more than one “IExecution” listener as you configure TestNG.

IHookable

You can use this interface when you don’t want to run the @Test method at that time. This interface provides a run method invoked when it finds an @Test method.

I love open-source technologies and am very passionate about software development. I like to share my knowledge with others, especially on technology that's why I have given all the examples as simple as possible to understand for beginners. All the code posted on my blog is developed, compiled, and tested in my development environment. If you find any mistakes or bugs, Please drop an email to softwaretestingo.com@gmail.com, or You can join me on Linkedin.

2 thoughts on “TestNG Listeners – Selenium WebDriver”

Leave a Comment