aboutsummaryrefslogtreecommitdiff
path: root/doc/selenium.html
blob: dadc41dfe63ad96e9f02c14523f7c74eb15e4733 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276


<html>
    <head>
        <title>TestNG</title>

        <link rel="stylesheet" href="testng.css" type="text/css" />
        <link type="text/css" rel="stylesheet" href="http://beust.com/beust.css"  />
        <script type="text/javascript" src="banner.js"></script>

      <script type="text/javascript" src="http://beust.com/scripts/shCore.js"></script>
      <script type="text/javascript" src="http://beust.com/scripts/shBrushJava.js"></script>
      <script type="text/javascript" src="http://beust.com/scripts/shBrushXml.js"></script>
      <script type="text/javascript" src="http://beust.com/scripts/shBrushBash.js"></script>
      <script type="text/javascript" src="http://beust.com/scripts/shBrushPlain.js"></script>
      <link type="text/css" rel="stylesheet" href="http://beust.com/styles/shCore.css"/>
      <link type="text/css" rel="stylesheet" href="http://beust.com/styles/shThemeCedric.css"/>
      <script type="text/javascript">
        SyntaxHighlighter.config.clipboardSwf = 'scripts/clipboard.swf';
        SyntaxHighlighter.defaults['gutter'] = false;
        SyntaxHighlighter.all();
      </script>

        <style type="text/css">
            /* Set the command-line table option column width. */
            #command-line colgroup.option {
                 width: 7em;
            }
        </style>
    </head>
<body onLoad="prettyPrint()">

<script type="text/javascript">
    displayMenu("selenium.html")
</script>

<h2 align="center">Selenium and TestNG</h2>

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


<h3>Content</h3>

<ol>
  <li><a href="#modeling">How to use TestNG configuration methods with parameters</a>
  <li><a href="#configuration_methods">How to configure your test</a>
  <li><a href="#creating_xml">Creating the XML file for TestNG</a>
  <li><a href="#launching">Lauching your tests with Eclipse</a>
  <li><a href="#future">How to make the test design a little better for the future</a>
</ol>

<h3><a name="#modeling">Modeling your test case</a></h3>

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

<ol>
  
  <li>Go to <a href="http://demo.opensourcecms.com/wordpress/wp-login.php">http://demo.opensourcecms.com/wordpress/wp-login.php</a>
  <li>Enter "admin" in the "Username" field
  <li>Enter "demo123" in the "Password" field
  <li>Click on the "Log In" button
  <li>Verify that the text "Howdy, admin" is present
  <li>Click on the "Posts" link
  <li>Click on the "Add New" button
  <li>Type "Selenium Demo Post" in the title field
  <li>Click on the "Publish" button
  <li> Verify that the text "Post published" is present

</ol>

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

<p>This is how I would break down the test:</p>

<ol>
  <li>Launch the WordPress site
  <li>Open the Admin Login page
  <li>Enter valid login data
  <li>Navigate to the Write Post page
  <li>Write the post
  <li>Publish the post
  <li>Verify that it was actually post
</ol>

<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> 
<p>Let's see how to do that with actual Java code:</p>

<pre class="brush:java">
@Test(description="Launches the WordPress site")
public void launchSite(){
  selenium.open("");
  selenium.waitForPageToLoad("30000");
  assertEquals(selenium.getTitle(), "Demo | Just another WordPress site");
}
 
@Test(description="Navigates to the admin page")
  public void openAdminPage() {
  selenium.open("wp-admin");
  selenium.waitForPageToLoad("30000");
  assertEquals(selenium.getTitle(), "Demo › Log In");
}
 
@Test(description="Enters valid login data")
  public void loginAsAdmin() {
  selenium.type("user_login", "admin");
  selenium.type("user_pass", "demo123");
  selenium.click("wp-submit");
  selenium.waitForPageToLoad("30000");
  assertTrue(selenium.isTextPresent("Howdy, admin"));
}
 
@Test(description="Navigates to the New Post screen")
public void navigateNewPost() {
  selenium.click("//a[contains(text(),'Posts')]/following::a[contains(text(),'Add New')][1]");
  selenium.waitForPageToLoad("30000");
  assertTrue(selenium.isTextPresent("Add New Post"));
}
 
@Test(description="Writes the new post")
public void writeBlogPost() {
  selenium.type("title", "New Blog Post");
  selenium.click("edButtonHTML");
  selenium.type("content", "This is a new post");
  //TODO:Assert
}
 
@Test(description="Publishes the post")
public void publishBlogPost() {
  selenium.click("submitdiv");
  selenium.click("publish");
  selenium.waitForPageToLoad("30000");
  assertTrue(selenium.isTextPresent("Post published."));
}
 
@Test(description="Verifies the post")
public void verifyBlogPost() {
  selenium.click("//a[contains(text(),'Posts') and contains(@class,'wp-first-item')]");
  selenium.waitForPageToLoad("30000");
  assertTrue(selenium.isElementPresent("//a[text()='New Blog Post']"));
}
 
@Test(description="Logs out")
public void logout() {
  selenium.click("//a[text()='Log Out']");
  //TODO:Assert
}
</pre>

<p>These are the test methods (or steps) we are going to use.

<h3><a name="configuration_methods">Configuration methods</a></h3>

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

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

<pre class="brush:java "> 
@BeforeSuite(alwaysRun = true)
public void setupBeforeSuite(ITestContext context) {
  String seleniumHost = context.getCurrentXmlTest().getParameter("selenium.host");
  String seleniumPort = context.getCurrentXmlTest().getParameter("selenium.port");
  String seleniumBrowser = context.getCurrentXmlTest().getParameter("selenium.browser");
  String seleniumUrl = context.getCurrentXmlTest().getParameter("selenium.url");
 
  RemoteControlConfiguration rcc = new RemoteControlConfiguration();
  rcc.setSingleWindow(true);
  rcc.setPort(Integer.parseInt(seleniumPort));
 
  try {
    server = new SeleniumServer(false, rcc);
    server.boot();
  } catch (Exception e) {
    throw new IllegalStateException("Can't start selenium server", e);
  }
 
  proc = new HttpCommandProcessor(seleniumHost, Integer.parseInt(seleniumPort),
      seleniumBrowser, seleniumUrl);
  selenium = new DefaultSelenium(proc);
  selenium.start();
}
 
@AfterSuite(alwaysRun = true)
public void setupAfterSuite() {
  selenium.stop();
  server.stop();
}
</pre>

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

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

<h3><a name="creating_xml">Creating the XML file</a></h3>

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

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

<p>Here is the XML file:</p>

<pre class="brush: xml">
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">

<suite name="Knorrium.info - Wordpress Demo" verbose="10">
  &lt;parameter name="selenium.host" value="localhost" /&gt;
  &lt;parameter name="selenium.port" value="3737" /&gt;
  &lt;parameter name="selenium.browser" value="*firefox" /&gt;
  &lt;parameter name="selenium.url" value="http://demo.opensourcecms.com/wordpress/" /&gt;

  <test name="Write new post" preserve-order="true">
    <classes>
      <class name="test.Wordpress">
        <methods>
          &lt;include name="launchSite" /&gt;
          &lt;include name="openAdminPage" /&gt;
          &lt;include name="loginAsAdmin" /&gt;
          &lt;include name="navigateNewPost" /&gt;
          &lt;include name="writeBlogPost" /&gt;
          &lt;include name="publishBlogPost" /&gt;
          &lt;include name="verifyBlogPost" /&gt;
        </methods>
      </class>
    </classes>
  </test>
</suite>
</pre>

<h3><a name="launching">Launching your tests in Eclipse</a></h3>

<p>We finished writing our tests, now how can we run them?</p> 
<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>

<p>If you installed TestNG correctly, you will see this menu when you right click on the XML file:<br />

<p align="center">
  <a href="http://testng.org/pictures/testNG-run.png">
    <img src="http://testng.org/pictures/testNG-run.png" alt="" title="testNG-run" width="464" height="59" class="aligncenter size-full wp-image-19" />
</a>
</p>

<p>Click on &#8220;Run as TestNG Suite&#8221; and your test will start running. You will then see this nice results tree:</p>

<p align="center">
  <a href="http://testng.org/pictures/testNG-exec.png">
    <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>
</p>

<h3><a name="future">Thinking about the future</a></h3>


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

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

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


<pre class="brush: java">
public class WordPressAdmin extends BaseTest {
@Test
public void test1(){
  selenium.open("");
  //...
}

@Test
public void test2(){
  selenium.open("");
  //...
}
}
</pre>

<p>This is better than leaving your configuration methods in the test class.</p>