Home | History | Annotate | Download | only in doc
      1 
      2 
      3 <html>
      4     <head>
      5         <title>TestNG</title>
      6 
      7         <link rel="stylesheet" href="testng.css" type="text/css" />
      8         <link type="text/css" rel="stylesheet" href="http://beust.com/beust.css"  />
      9         <script type="text/javascript" src="banner.js"></script>
     10 
     11       <script type="text/javascript" src="http://beust.com/scripts/shCore.js"></script>
     12       <script type="text/javascript" src="http://beust.com/scripts/shBrushJava.js"></script>
     13       <script type="text/javascript" src="http://beust.com/scripts/shBrushXml.js"></script>
     14       <script type="text/javascript" src="http://beust.com/scripts/shBrushBash.js"></script>
     15       <script type="text/javascript" src="http://beust.com/scripts/shBrushPlain.js"></script>
     16       <link type="text/css" rel="stylesheet" href="http://beust.com/styles/shCore.css"/>
     17       <link type="text/css" rel="stylesheet" href="http://beust.com/styles/shThemeCedric.css"/>
     18       <script type="text/javascript">
     19         SyntaxHighlighter.config.clipboardSwf = 'scripts/clipboard.swf';
     20         SyntaxHighlighter.defaults['gutter'] = false;
     21         SyntaxHighlighter.all();
     22       </script>
     23 
     24         <style type="text/css">
     25             /* Set the command-line table option column width. */
     26             #command-line colgroup.option {
     27                  width: 7em;
     28             }
     29         </style>
     30     </head>
     31 <body onLoad="prettyPrint()">
     32 
     33 <script type="text/javascript">
     34     displayMenu("selenium.html")
     35 </script>
     36 
     37 <h2 align="center">Selenium and TestNG</h2>
     38 
     39 <em>This documentation was written by Felipe Knorr Kuhn and is adapted from <a href="http://knorrium.info/2010/08/31/using-testng-to-launch-your-tests-and-the-selenium-server/">a series of articles</a> posted on <a href="http://knorrium.info">his blog</a></em>.
     40 
     41 
     42 <h3>Content</h3>
     43 
     44 <ol>
     45   <li><a href="#modeling">How to use TestNG configuration methods with parameters</a>
     46   <li><a href="#configuration_methods">How to configure your test</a>
     47   <li><a href="#creating_xml">Creating the XML file for TestNG</a>
     48   <li><a href="#launching">Lauching your tests with Eclipse</a>
     49   <li><a href="#future">How to make the test design a little better for the future</a>
     50 </ol>
     51 
     52 <h3><a name="#modeling">Modeling your test case</a></h3>
     53 
     54 <p>Before writing a test case, you need to know how and what will be validated. Let's use the WordPress <a href="http://knorrium.info/2010/05/19/a-java-approach-to-selenium">"Create New Post" test case</a>.
     55 
     56 <ol>
     57   
     58   <li>Go to <a href="http://demo.opensourcecms.com/wordpress/wp-login.php">http://demo.opensourcecms.com/wordpress/wp-login.php</a>
     59   <li>Enter "admin" in the "Username" field
     60   <li>Enter "demo123" in the "Password" field
     61   <li>Click on the "Log In" button
     62   <li>Verify that the text "Howdy, admin" is present
     63   <li>Click on the "Posts" link
     64   <li>Click on the "Add New" button
     65   <li>Type "Selenium Demo Post" in the title field
     66   <li>Click on the "Publish" button
     67   <li> Verify that the text "Post published" is present
     68 
     69 </ol>
     70 
     71 <p>Considering this scenario, the first thing that comes to mind is creating a long test case that goes through all the steps. This might be a good approach if you are writing a manual test case. However, since we are writing an automated test, we want to write our script as modular as possible to be able to reuse parts of it in future scenarios.</p>
     72 
     73 <p>This is how I would break down the test:</p>
     74 
     75 <ol>
     76   <li>Launch the WordPress site
     77   <li>Open the Admin Login page
     78   <li>Enter valid login data
     79   <li>Navigate to the Write Post page
     80   <li>Write the post
     81   <li>Publish the post
     82   <li>Verify that it was actually post
     83 </ol>
     84 
     85 <p>Keep in mind that this is just an example. You are free to model your tests in any way you want, as long as they have business value and will validate your business logic.</p> 
     86 <p>Let's see how to do that with actual Java code:</p>
     87 
     88 <pre class="brush:java">
     89 @Test(description="Launches the WordPress site")
     90 public void launchSite(){
     91   selenium.open("");
     92   selenium.waitForPageToLoad("30000");
     93   assertEquals(selenium.getTitle(), "Demo | Just another WordPress site");
     94 }
     95  
     96 @Test(description="Navigates to the admin page")
     97   public void openAdminPage() {
     98   selenium.open("wp-admin");
     99   selenium.waitForPageToLoad("30000");
    100   assertEquals(selenium.getTitle(), "Demo  Log In");
    101 }
    102  
    103 @Test(description="Enters valid login data")
    104   public void loginAsAdmin() {
    105   selenium.type("user_login", "admin");
    106   selenium.type("user_pass", "demo123");
    107   selenium.click("wp-submit");
    108   selenium.waitForPageToLoad("30000");
    109   assertTrue(selenium.isTextPresent("Howdy, admin"));
    110 }
    111  
    112 @Test(description="Navigates to the New Post screen")
    113 public void navigateNewPost() {
    114   selenium.click("//a[contains(text(),'Posts')]/following::a[contains(text(),'Add New')][1]");
    115   selenium.waitForPageToLoad("30000");
    116   assertTrue(selenium.isTextPresent("Add New Post"));
    117 }
    118  
    119 @Test(description="Writes the new post")
    120 public void writeBlogPost() {
    121   selenium.type("title", "New Blog Post");
    122   selenium.click("edButtonHTML");
    123   selenium.type("content", "This is a new post");
    124   //TODO:Assert
    125 }
    126  
    127 @Test(description="Publishes the post")
    128 public void publishBlogPost() {
    129   selenium.click("submitdiv");
    130   selenium.click("publish");
    131   selenium.waitForPageToLoad("30000");
    132   assertTrue(selenium.isTextPresent("Post published."));
    133 }
    134  
    135 @Test(description="Verifies the post")
    136 public void verifyBlogPost() {
    137   selenium.click("//a[contains(text(),'Posts') and contains(@class,'wp-first-item')]");
    138   selenium.waitForPageToLoad("30000");
    139   assertTrue(selenium.isElementPresent("//a[text()='New Blog Post']"));
    140 }
    141  
    142 @Test(description="Logs out")
    143 public void logout() {
    144   selenium.click("//a[text()='Log Out']");
    145   //TODO:Assert
    146 }
    147 </pre>
    148 
    149 <p>These are the test methods (or steps) we are going to use.
    150 
    151 <h3><a name="configuration_methods">Configuration methods</a></h3>
    152 
    153 <p>If you are familiar with unit testing frameworks, you probably know about the setup and teardown methods. TestNG goes beyond that idea and allows you to define methods that will be run after or before your test suites, test groups or test methods. This is very useful for our Selenium tests because you can create a Selenium server and browser instance before you start running your test suite.)</p>
    154 
    155 <p>To achieve this, we will use two TestNG <a href="http://testng.org/doc/documentation-main.html#annotations">annotations</a>: <tt>@BeforeSuite</tt> and <tt>@AfterSuite</tt>:</p>
    156 
    157 <pre class="brush:java "> 
    158 @BeforeSuite(alwaysRun = true)
    159 public void setupBeforeSuite(ITestContext context) {
    160   String seleniumHost = context.getCurrentXmlTest().getParameter("selenium.host");
    161   String seleniumPort = context.getCurrentXmlTest().getParameter("selenium.port");
    162   String seleniumBrowser = context.getCurrentXmlTest().getParameter("selenium.browser");
    163   String seleniumUrl = context.getCurrentXmlTest().getParameter("selenium.url");
    164  
    165   RemoteControlConfiguration rcc = new RemoteControlConfiguration();
    166   rcc.setSingleWindow(true);
    167   rcc.setPort(Integer.parseInt(seleniumPort));
    168  
    169   try {
    170     server = new SeleniumServer(false, rcc);
    171     server.boot();
    172   } catch (Exception e) {
    173     throw new IllegalStateException("Can't start selenium server", e);
    174   }
    175  
    176   proc = new HttpCommandProcessor(seleniumHost, Integer.parseInt(seleniumPort),
    177       seleniumBrowser, seleniumUrl);
    178   selenium = new DefaultSelenium(proc);
    179   selenium.start();
    180 }
    181  
    182 @AfterSuite(alwaysRun = true)
    183 public void setupAfterSuite() {
    184   selenium.stop();
    185   server.stop();
    186 }
    187 </pre>
    188 
    189 <p>PS: Did you notice those weird parameters? They are stored in the XML file (we are going to see in the next section) and accessed by a <tt>ITestContext</tt> object, which was <a href="http://testng.org/doc/documentation-main.html#dependency-injection">injected</a>. </p>
    190 
    191 <p>By adding these annotations, the TestNG engine will invoke the configuration methods automatically before/after your test suite (make sure the test methods are annotated with <tt>@Test</tt>), launching the Selenium server and instantiating the Selenium client object only once, reusing the same browser session across the tests.</p>
    192 
    193 <h3><a name="creating_xml">Creating the XML file</a></h3>
    194 
    195 <p>To define the order of the tests, we will have to create an XML file listing the test methods we would like to run. Make sure that the test methods are annotated with <tt>@Test</tt>, or else the TestNG engine will not invoke them.</p>
    196 
    197 <p>Before TestNG 5.13.1, you had to use Method Interceptors if you wanted to run the tests in the order defined in the XML file. I have posted <a href="http://gist.github.com/416310">my implementation of a Method Interceptor</a> on my Github account. From TestNG 5.13.1+, you can just add the <tt>preserve-order</tt> parameter to your test tag and include the methods you would like to run, reducing unecessary code in your test suite.</p>
    198 
    199 <p>Here is the XML file:</p>
    200 
    201 <pre class="brush: xml">
    202 <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
    203 
    204 <suite name="Knorrium.info - Wordpress Demo" verbose="10">
    205   &lt;parameter name="selenium.host" value="localhost" /&gt;
    206   &lt;parameter name="selenium.port" value="3737" /&gt;
    207   &lt;parameter name="selenium.browser" value="*firefox" /&gt;
    208   &lt;parameter name="selenium.url" value="http://demo.opensourcecms.com/wordpress/" /&gt;
    209 
    210   <test name="Write new post" preserve-order="true">
    211     <classes>
    212       <class name="test.Wordpress">
    213         <methods>
    214           &lt;include name="launchSite" /&gt;
    215           &lt;include name="openAdminPage" /&gt;
    216           &lt;include name="loginAsAdmin" /&gt;
    217           &lt;include name="navigateNewPost" /&gt;
    218           &lt;include name="writeBlogPost" /&gt;
    219           &lt;include name="publishBlogPost" /&gt;
    220           &lt;include name="verifyBlogPost" /&gt;
    221         </methods>
    222       </class>
    223     </classes>
    224   </test>
    225 </suite>
    226 </pre>
    227 
    228 <h3><a name="launching">Launching your tests in Eclipse</a></h3>
    229 
    230 <p>We finished writing our tests, now how can we run them?</p> 
    231 <p>You can launch TestNG from the command line, using a Eclipse plugin or even programatically. We are going to use the Eclipse plugin. Follow the steps described on the official TestNG documentation <a href="http://testng.org/doc/download.html">over here</a></p>
    232 
    233 <p>If you installed TestNG correctly, you will see this menu when you right click on the XML file:<br />
    234 
    235 <p align="center">
    236   <a href="http://testng.org/pictures/testNG-run.png">
    237     <img src="http://testng.org/pictures/testNG-run.png" alt="" title="testNG-run" width="464" height="59" class="aligncenter size-full wp-image-19" />
    238 </a>
    239 </p>
    240 
    241 <p>Click on &#8220;Run as TestNG Suite&#8221; and your test will start running. You will then see this nice results tree:</p>
    242 
    243 <p align="center">
    244   <a href="http://testng.org/pictures/testNG-exec.png">
    245     <img src="http://testng.org/pictures/testNG-exec.png" alt="" title="testNG-exec" width="591" height="256" class="aligncenter size-full wp-image-20" /></a>
    246 </p>
    247 
    248 <h3><a name="future">Thinking about the future</a></h3>
    249 
    250 
    251 <p>If you really want to think about the future of your test suite, I would recommend you to read <a href="http://adam.goucher.ca/">Adam</a> <a href="http://twitter.com/adamgoucher">Goucher&#8217;s</a> <a href="http://www.pragprog.com/magazines">article</a> published on PragPub. He talks about Selenium 2 and the Page Objects Model (a very nice way to model your tests, especially if you use Selenium 2).</p>
    252 
    253 <p>Since there are lots of people still using Selenium 1, I'll stick to that for a while, but Selenium 2 will eventually be covered here.</p>
    254 
    255 <p>As the number of tests in your test suite grows, you will find that grouping them in different test classes is a good idea. If you do that, you can take advantage of object oriented programming and create a new class named BaseTest (for example), and leave your configuration logic there. That way, every test class must extend the BaseTest class and use static attributes.</p>
    256 
    257 
    258 <pre class="brush: java">
    259 public class WordPressAdmin extends BaseTest {
    260 @Test
    261 public void test1(){
    262   selenium.open("");
    263   //...
    264 }
    265 
    266 @Test
    267 public void test2(){
    268   selenium.open("");
    269   //...
    270 }
    271 }
    272 </pre>
    273 
    274 <p>This is better than leaving your configuration methods in the test class.</p>
    275 
    276 
    277