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...
“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.
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