Thursday, December 11, 2014

TestNG custom listeners



TestNG custom listeners: ITestListener


When running TestNG tests, one could want to perform some common actions – after each test has finished successfully, after each failed test, after each skipped test, or after all the tests have finished running, no matter their result. To apply such a common behavior to a group of tests, a custom listener can be created, that implements TestNG’s ITestListener interface. There are two types of methods to implement: a set of them are related to a single test’s run (onTestStart, onTestSuccess, onTestFailure, onTestSkipped, onTestFailedButWithinSuccessPercentage), the other to the whole suite’s run (onStart, onFinish). These methods, depending on their type, have a parameter passed to them: result, which is of type ITestResult – for the test run, and context, which is of type ITestContext, for the suite run. These types offer different information, for example: ITestResult can tell you when a test began to run, when it finished running, what status the test had (whether failed, passed), whereas ITestContext can tell you the list of all the passed tests, all the failed ones, and so on.

The custom listener that will be used must implement all the ITestListener interface’s methods, as depicted below:
public class SimpleTestListener implements ITestListener{

@Override
public void onTestStart(ITestResult result) {
}

@Override
public void onTestSuccess(ITestResult result) {
}

@Override
public void onTestFailure(ITestResult result) {
}

@Override
public void onTestSkipped(ITestResult result) {
}

@Override
public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
}

@Override
public void onStart(ITestContext context) {
}

@Override
public void onFinish(ITestContext context) {
}
 As a simple example, one would like for a test to print the name of it’s method, the time when it started running (in milliseconds), and the result of the run.  In this case, the listener’s implemented methods would look something like:
@Override
public void onTestStart(ITestResult result) {
        System.out.println("Teststarted running:"  + result.getMethod().getMethodName() + " at:" + result.getStartMillis());
}

@Override
public void onTestSuccess(ITestResult result) {
   System.out.println("Result success");
}

@Override
public void onTestFailure(ITestResult result) {
   System.out.println("Result failure");
}

@Override
public void onTestSkipped(ITestResult result) {
   System.out.println("Testskipped!");
}
Also, the list of failed and list of passed tests should be displayed. In this case, the listener’s implemented methods would look something like:
@Override
public void onFinish(ITestContext context) {
System.out.println("Passeds: " + context.getPassedTests());                      System.out.println("Faileds:" + context.getFailedTests());
}
Using the listener
Now, after the listener has been implemented, it needs to be declared so that the tests know how to use it. There are two ways of doing that: the first one is by adding a @Listeners annotation to the tests that need to behave as the listener says. The disadvantage here is that, if there are many test classes that need to use the listener, each test class must introduce the new annotation. This might be a more cumbersome job. The advantage is that if you want to run this class from your IDE (not from the command line), by using the annotation, the listener will be applied to this class. Such a test would look something like (note that the @Listeners annotation must be placed above a @Test annotation):
@Listeners(CustomTestListener.class)
@Test
public void someTest() {
...
}
However, if tests are run from an .xml configuration file (as described in a ‘listeners’ section can be added to this file,  in the ‘suite’ section, right above the ‘test’ section, so that all the tests that are run by using this file will by default use the new listener.  Note that if you take this approach, when running any test from the IDE directly (with ‘Run as TestNG test’), the listener will not be applied. It applies only when running tests by specifying this configuration file. The listeners section would look like:
<listeners>
<listener class-name="path.To.The.Listener.CustomTestListener" />
</listeners>





//
you're reading...
TestNG

“Listen” to what I have to say about “TestNG” Listeners

What are these listeners ? What does TestNG stand to offer to us with them ? This question is something that someone seemed to have. So thought I might as well add up my “2 cents” to un-ravelling its mysteries.
Although I would try my level best to explain what it is, one must never forget that I regard Cedric’s “Next Generation Testing” as my Bible and all that I am now going to blabber have been because I managed to read this book. Hat’s off to Cedric for that ! :)
In-case you have done/or exposed to windows programming you would be familiar with the term “events”. Events are specific actions that happen on something for e.g., button clicked! is an event. A simple e.g., would be your mobile plays a sound everytime you get a call.. another event :) Incoming call is an “event” here and your mobile playing a tune is the result of an “event handler” being invoked :) [ Pats his back for that innovative e.g., hehehehe !! Smile will you.. am trying to take away the seriousness here.. sigh! ]
Listeners are basically those entities which are tuned into one or more events that may arise.
TestNG has awesomely managed to leverage this concept and provide a cleaner way of doing things.
So what are TestNG Listeners ?
They are basically “your” classes, which implement some interfaces that TestNG provides, so that when TestNG raises certain events it will look for all “classes” which are basically looking for such events and call the respective event handlers in them.. Confused ??
Its ok to be confused ! I was confused to the core too.
So lets start off with a sample program which will show how to use Listeners.
Here’s a sample Listener that I created, which basically takes care of Starting and Stopping a Selenium Server.
If you look at this listener, its no big complex thing. It is just an ordinary class that implements an Interface named “ISuiteListener”.
Whenever a class implements this listener, TestNG guarantees the end-user that it will invoke the methods onStart() and onFinish() before and after running a TestNG Suite.
So before TestNG picks up your suite for execution, it first makes a call to onStart() method and runs whatever has been scripted in this method. In a similar way, it again makes a call to onFinish() method after a suite has been run.
So what other listeners are there in TestNG ?
There are a lot of listeners. But I will give you an over view on the most common listeners that you would need :
  • org.testng.IReporter : Implement this listener within your class, if you want to customize the TestNG reports (for e.g., you might want your test reports to be available as an excel sheet or a word document or even a pdf for that matter). This would be the last of the calls that TestNG makes before closing your execution.
  • org.testng.IInvokedMethodListener : Implement this listener within your class, if you want something to be done before and after a method is invoked by TestNG. Some classic examples would be, instantiating and closing off the WebDriver for e.g.,.
  • org.testng.IInvokedMethodListener2 : Implement this listener within your class if you want all that IInvokedMethodListener can do, but also give you access to the ITestContext object. ITestContext object essentially is the contextual representation of all the relevant information for a given TestNG run.
  • org.testng.ITestListener : Implement this listener within your class if you want to be notified before and after a test (“<test>”) is run. This would also give you a way in which you can specify as to what should be done when a particular test is passed/skipped/failed etc., (A simple use case would be to implement some sort of a running commentary on the console indicating how many tests have so far run, how many passed and so on and so forth.
Ok, so now am guessing that you must be pretty aware of what listeners are all about and we also saw how to write a listener. Now comes the big question.
How do I let TestNG know that I have such a listener which it should invoke when it is executing my tests ?
There are essentially three ways of adding up a listener to a particular class. I will walk you through on all of the three ways [at-least these are the only ways I know of :) ] :
1. Using the @Listeners TestNG provided annotation.
This is the easiest way of binding your implemented TestNG Listener to your test class.
Before your class definition, you just add up this listener as below
@Listeners(SeleniumStarterListener.class)
public class IUseListeners {
 private Selenium selenium = null;
@BeforeClass
 public void setup() {
 selenium = new DefaultSelenium("localhost", 4444, "*firefox", "http://www.google.com");
 selenium.start();
 }
@Test
 public void f() {
 selenium.open("http://www.facebook.com}
@AfterClass
 public void tearDown() {
 selenium.stop();
 }
}
Here if you notice, I am telling TestNG that for my test class “IUseListeners” I need TestNG to refer to the class SeleniumStarterListener and invoke the corresponding methods within it as and when a relevant TestNG “triggered” event happens. So in this case, TestNG will first invoke my onStart() method in my Listener class before it begins executing my Test (remember, that if you dont provide a suite for your class as I have done here, TestNG creates a default suite, defines a default Test and adds up your testclass to it). That is why I when you execute this sample test along with the listener, the listener takes care of starting the selenium server and stopping it. Clean way of doing it isn’t it ?? That is what TestNG Listeners are there for :)
2. By using the <listeners> tag in your TestNG Template file.
Although approach 1 is more than enough to get you started, it’s not an “elegant” way of using Listeners, because you are forced to add this @Listeners section to each of your classes, which you perhaps wont want. So what you do is, you create a TestNG Suite file and then add up the listeners section to this suite file. That way, all of your tests would essentially leverage the listener that you wrote. So in our case, before the suite starts (remember a suite can contain one or more suites within itself, one or more tests within itself and each test<test> can have one or more test classes in it).
So here’s how a typical xml file would look like :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite" parallel="false">
 <listeners>
 <listener class-name="com.test.listeners.SeleniumStarterListener"/>
 </listeners>
 <test name="Test" preserve-order="false">
 <classes>
 <class name="com.test.IUseListeners" />
 </classes>
 </test>
</suite>
Pay close attention to the <listeners> section here. I have defined this listener at the suite level and have given the “fully qualified name” of my Listener class.
3. When you are working with TestNG class directly and want to pro-grammatically run your Tests you can specify your Listeners as below :
Here’s a sample code which shows you how to do this in a main() program of Java.
public static void main(String[] args){
 TestNG testng = new TestNG();
 Class[] classes = new Class[]{IUseListeners.class};
 testng.addListener(new SeleniumStarterListener());
 testng.setTestClasses(classes);
 testng.run();
 }

No comments:

Post a Comment

TestNG - Can i use the 2 different data providers to same @test methods in TestNG?

public Object [][] dp1 () { return new Object [][] { new Object [] { "a" , "b" }, new Obje...