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:
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:
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.
This is the best tutorial I found. Thanks a lot.
We Glad that it helps you