Skip to content
This repository was archived by the owner on Mar 10, 2020. It is now read-only.

Commit aded4fb

Browse files
committed
Added Getting Started tutorial and chapter on Ajax handling to documentation
1 parent 4165a6f commit aded4fb

File tree

5 files changed

+373
-0
lines changed

5 files changed

+373
-0
lines changed

documentation/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Welcome to the testIT WebTester documentation!
44

5+
- [Getting Started](chapters/getting-started.md)
56
- [Browser](chapters/browser.md)
67
- [Configuration](chapters/configuration.md)
78
- [Event System](chapters/event-system.md)
@@ -28,6 +29,7 @@
2829
- Integration
2930
- [Spring 4](chapters/support-spring4.md)
3031
- Advanced Topics
32+
- [JavaScript and Ajax Handling](chapters/javascript-ajax.md)
3133
- [Ad-Hoc finding of Page Objects](chapters/ad-hoc-find.md)
3234

3335

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
[Home](../README.md)
2+
3+
# Getting Started with WebTester
4+
5+
## What you'll create
6+
You'll write a simple GUI test for [Twitter](https://twitter.com/) using WebTester that tests basic login
7+
as well as tweeting a simple message. The end result will be similar to this:
8+
9+
![Example Tweet](../images/gs_tweet.png)
10+
11+
## What you'll need
12+
13+
- About 30 Minutes
14+
- Eclipse (You can use any IDE, but the guide is using Eclipse)
15+
- Java 1.7 or newer
16+
17+
## Create the project
18+
Open Eclipse and create a new Maven project. Leave the default values until the group and artifact Id need be to specified.
19+
Set the group Id to "info.novatec.testit" and the artifact Id to "webtester-example-twitter".
20+
Set up the project and create the files as depicted below.
21+
22+
![Example Project](../images/gs_project_structure.png)
23+
24+
Configure the Maven project to look like this and replace `${webtester-version}` with the version you want to use:
25+
26+
```xml
27+
<project
28+
xmlns="http://maven.apache.org/POM/4.0.0"
29+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
30+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
31+
<modelVersion>4.0.0</modelVersion>
32+
33+
<groupId>info.novatec.testit</groupId>
34+
<artifactId>webtester-example-twitter</artifactId>
35+
<version>0.0.1-SNAPSHOT</version>
36+
<packaging>jar</packaging>
37+
38+
<name>webtester-example-twitter</name>
39+
<url>https://documentation.novatec-gmbh.de/display/TESTIT</url>
40+
41+
<dependencies>
42+
<dependency>
43+
<groupId>info.novatec.testit</groupId>
44+
<artifactId>webtester-support-firefox</artifactId>
45+
<version>${webtester-version}</version>
46+
</dependency>
47+
<dependency>
48+
<groupId>info.novatec.testit</groupId>
49+
<artifactId>webtester-support-junit</artifactId>
50+
<version>${webtester-version}</version>
51+
</dependency>
52+
<dependency>
53+
<groupId>junit</groupId>
54+
<artifactId>junit</artifactId>
55+
<version>4.12</version>
56+
</dependency>
57+
<dependency>
58+
<groupId>ch.qos.logback</groupId>
59+
<artifactId>logback-classic</artifactId>
60+
<version>1.1.3</version>
61+
</dependency>
62+
</dependencies>
63+
64+
<repositories>
65+
<repository>
66+
<id>novatec</id>
67+
<name>NovaTec Public Repository</name>
68+
<url>http://repository.novatec-gmbh.de/content/repositories/novatec/</url>
69+
</repository>
70+
</repositories>
71+
72+
<build>
73+
<plugins>
74+
<plugin>
75+
<groupId>org.apache.maven.plugins</groupId>
76+
<artifactId>maven-compiler-plugin</artifactId>
77+
<version>3.1</version>
78+
<configuration>
79+
<source>1.7</source>
80+
<target>1.7</target>
81+
<compilerVersion>1.7</compilerVersion>
82+
<encoding>UTF8</encoding>
83+
<showWarnings>true</showWarnings>
84+
<showDeprecation>true</showDeprecation>
85+
</configuration>
86+
</plugin>
87+
</plugins>
88+
</build>
89+
90+
</project>
91+
```
92+
93+
Create a new file: `testit-webtester.properties` and set your twitter credentials:
94+
```properties
95+
twitter.username=INSERT_TWITTER_USERNAME
96+
twitter.password=INSERT_TWITTER_PASSWORD
97+
```
98+
99+
## Create PageObject for Login Page
100+
After you've set up the project it's time to start writing some code.
101+
102+
To login into your Twitter account we need to setup a PageObject for the Login Page. Open the TwitterLogin
103+
class and have it extend the PageObject class. Now we need to find the respective HTML elements to interact
104+
with on the Twitter page. WebTester can identify Objects by id, class, xpath or css using Selenium. After inspecting
105+
the HTML source of the Twitter Login Page you'll need to find the Objects you can interact with. In this case the login
106+
button, the username field and the password field. We'll identify these two fields by id, while we'll use the class to
107+
get the login button.
108+
109+
We need to make sure that we are on the correct page @PostConstruct of the PageObject. To achieve this the page title
110+
is matched with the title we expect. In our case the title should be the following.
111+
112+
> Welcome to Twitter - Login or Sign up
113+
114+
For all the gory details, please view the source code below at line 23.
115+
116+
For the login process we need the following methods: setUsername, setPassword, clickLogin, and login. Note that these
117+
methods return type must extend PageObject. It is a representation of the state of the page opened after the methods
118+
execution.
119+
120+
The methods setUsername and setPassword return instances of TwitterLogin while clickLogin and login return instances
121+
of TwitterHome.
122+
123+
```java
124+
package pageobjects;
125+
126+
import static org.junit.Assert.assertEquals;
127+
128+
import info.novatec.testit.webtester.api.annotations.AfterInitialization;
129+
import info.novatec.testit.webtester.api.annotations.IdentifyUsing;
130+
import info.novatec.testit.webtester.api.enumerations.Method;
131+
import info.novatec.testit.webtester.pageobjects.Button;
132+
import info.novatec.testit.webtester.pageobjects.PageObject;
133+
import info.novatec.testit.webtester.pageobjects.PasswordField;
134+
import info.novatec.testit.webtester.pageobjects.TextField;
135+
136+
137+
public class TwitterLogin extends PageObject {
138+
139+
@IdentifyUsing ( method = Method.CLASS, value = "primary-btn" )
140+
private Button loginButton;
141+
@IdentifyUsing ( "signin-email" )
142+
private TextField usernameField;
143+
@IdentifyUsing ( "signin-password" )
144+
private PasswordField passwordField;
145+
146+
@PostConstruct
147+
private void assertThatCorrectPageIsDisplayed () {
148+
assertEquals("Welcome to Twitter - Login or Sign up", getBrowser().getPageTitle());
149+
}
150+
151+
public TwitterHome login (String username, String password) {
152+
return setUsername(username).setPassword(password).clickLogin();
153+
}
154+
155+
public TwitterLogin setUsername (String username) {
156+
usernameField.setText(username);
157+
return this;
158+
}
159+
160+
public TwitterLogin setPassword (String password) {
161+
passwordField.setText(password);
162+
return this;
163+
}
164+
165+
public TwitterHome clickLogin () {
166+
loginButton.click();
167+
return create(TwitterHome.class);
168+
}
169+
170+
}
171+
```
172+
173+
**Note:** When using `@IdentifyUsing` with an Id, it's not necessary to explicitly define the method used as Id is the default.
174+
175+
## Create PageObject for Home Page
176+
177+
Similar to the Login Page we create a PageObject representation for the Twitter Home Page.
178+
179+
```java
180+
package pageobjects;
181+
182+
import static org.junit.Assert.assertEquals;
183+
184+
import info.novatec.testit.webtester.api.annotations.AfterInitialization;
185+
import info.novatec.testit.webtester.api.annotations.IdentifyUsing;
186+
import info.novatec.testit.webtester.api.enumerations.Method;
187+
import info.novatec.testit.webtester.pageobjects.Button;
188+
import info.novatec.testit.webtester.pageobjects.PageObject;
189+
import pageobjects.widgets.TweetBox;
190+
191+
192+
public class TwitterHome extends PageObject {
193+
194+
@IdentifyUsing ( "tweet-box-home-timeline" )
195+
private TweetBox tweetBox;
196+
@IdentifyUsing ( method = Method.CSS, value =
197+
".btn.primary-btn.tweet-action.tweet-btn.js-tweet-btn" )
198+
private Button sendTweetButton;
199+
@IdentifyUsing ( method = Method.XPATH, value =
200+
".//ol[@id='stream-items-id']/li[1]//p[contains(@class, 'tweet-text')]" )
201+
private PageObject latestTweet;
202+
203+
@PostConstruct
204+
private void assertThatCorrectPageIsDisplayed () {
205+
assertEquals("Twitter", getBrowser().getPageTitle());
206+
}
207+
208+
public TwitterHome tweet (String message) {
209+
setTweetMessage(message).sendTweet();
210+
return this;
211+
}
212+
213+
public TwitterHome setTweetMessage (String message) {
214+
tweetBox.setMessage(message);
215+
return this;
216+
}
217+
218+
public TwitterHome sendTweet () {
219+
sendTweetButton.click();
220+
return create(TwitterHome.class);
221+
}
222+
223+
public String getMessageOfLatesTweet () {
224+
return latestTweet.getVisibleText();
225+
}
226+
227+
}
228+
```
229+
230+
As PageObjects can represent either entire pages or just a certain part of it, we use the TweetBox class as a
231+
representation of Twitter´s TweetBox.
232+
233+
```java
234+
package pageobjects.widgets;
235+
236+
import info.novatec.testit.webtester.pageobjects.PageObject;
237+
238+
239+
public class TweetBox extends PageObject {
240+
241+
public TweetBox setMessage (String message) {
242+
click();
243+
getWebElement().sendKeys(message);
244+
return this;
245+
}
246+
247+
}
248+
```
249+
250+
## Create the Test
251+
252+
The `TwitterTest` must `@RunWith` the `WebTesterJUnitRunner` class for JUnit support.
253+
254+
In this example the `@CreateUsing` annotation specifies the `Browser` to be an instance of Firefox.
255+
256+
The `@EntryPoint` is the initial page used by WebTester. It´s the first page the configured browser navigates to.
257+
258+
The Twitter password and username are taken from the `testit-webtester.properties` file using the `@ConfigurationValue`
259+
annotation.
260+
261+
`@Before` test execution a representational instance of the Twitter Login page (twitter.com) is returned by the
262+
`Browser` as a `TwitterLogin` (created above) object.
263+
264+
During the `@Test` WebTester will use twitterLogin to log in with the username and password specified in the
265+
properties file. After a successful login a tweet containing the current system time is tweeted and the test then
266+
asserts that the tweet has been tweeted.
267+
268+
```java
269+
package test;
270+
271+
import static org.junit.Assert.assertEquals;
272+
273+
import java.util.Date;
274+
275+
import javax.annotation.Resource;
276+
277+
import org.junit.Before;
278+
import org.junit.Test;
279+
import org.junit.runner.RunWith;
280+
281+
import info.novatec.testit.webtester.utils.Waits;
282+
import info.novatec.testit.webtester.api.browser.Browser;
283+
import info.novatec.testit.webtester.browser.factories.FirefoxFactory;
284+
import info.novatec.testit.webtester.junit.annotations.ConfigurationValue;
285+
import info.novatec.testit.webtester.junit.annotations.CreateUsing;
286+
import info.novatec.testit.webtester.junit.annotations.EntryPoint;
287+
import info.novatec.testit.webtester.junit.runner.WebTesterJUnitRunner;
288+
import pageobjects.TwitterHome;
289+
import pageobjects.TwitterLogin;
290+
291+
292+
@RunWith ( WebTesterJUnitRunner.class )
293+
public class TwitterTest {
294+
295+
@Resource
296+
@CreateUsing ( FirefoxFactory.class )
297+
@EntryPoint ( "https://twitter.com/" )
298+
private Browser browser;
299+
300+
@ConfigurationValue ( "twitter.username" )
301+
private String username;
302+
@ConfigurationValue ( "twitter.password" )
303+
private String password;
304+
305+
private TwitterLogin twitterLogin;
306+
307+
@Before
308+
public void initStartPage () {
309+
twitterLogin = browser.create(TwitterLogin.class);
310+
}
311+
312+
@Test
313+
public void testThatMessageCanBeTweeted () throws InterruptedException {
314+
315+
String tweetMessage = "It is " + new Date();
316+
TwitterHome home = twitterLogin.login(username, password).tweet(tweetMessage);
317+
318+
Waits.waitSeconds(1); // AJAX actions take some time to be performed
319+
assertEquals(tweetMessage, home.getMessageOfLatesTweet());
320+
321+
}
322+
323+
}
324+
```
325+
326+
## Summary
327+
Congratulations! You´ve just developed a simple Twitter login and tweet test with WebTester.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
[Home](../README.md)
2+
3+
# AJAX
4+
JavaScript heavy applications pose special problems when testing through the UI. They tend to lose their `WebElement`
5+
references a lot. Resulting in occurrences of `StaleElementReferenceException`. WebTester as multiple ways of dealing with
6+
these problems.
7+
8+
The first being an automatic retry mechanism: Every action of a `PageObject` executed by WebTester will automatically be
9+
retried in case a `StaleElementReferenceException` occurred. This should provide base stability in simple JavaScript
10+
applications. Depending on your application there are a number of additional steps you can take to stabilize your tests in
11+
these difficult environments.
12+
13+
## Manually Invalidating the PageObject's Cache
14+
One approach to handling asynchronous changes to your page objects is to invalidate those you know can change,
15+
before interacting with them. This can be done by using the `Invalidator` utility class. It clears the `WebElement` cache
16+
of `PageObject`, a `List<PageObject>` as well as `PageObjectList` instances.
17+
18+
- Invalidating a `PageObject` instance will result in the recursive invalidation of all of the page object's fields.
19+
- Invalidating a `List<PageObject>` instance will result in the invalidation of each page object of the list.
20+
- Invalidating a `PageObjectList` instance will result in the invalidation of the list it self.
21+
22+
> Since this approach adds certain overhead to your test code it is only recommended for applications with low JavaScript
23+
/ AJAX use.
24+
25+
## Deactivating the PageObject's Cache
26+
If you known which elements of a page change frequently you can deactivate the cache for those elements by setting the
27+
`caching` property of their `@IdentifyUsing` annotation to `OFF`. This will force the object to look up it's corresponding
28+
`WebElement` every time a method is called. This will add a certain overhead to your test execution time but will
29+
guarantee that the elements are always usable.
30+
31+
> This approach is recommended for applications with a medium amount of JavaScript / AJAX.
32+
33+
## Global Deactivation of the PageObject Cache
34+
In cases where the application under test is solely based on AJAX it is possible to deactivate the `PageObject`'s
35+
`WebElement` caching by default. This can be done by setting the `pageobjects.caching` property to `false`. It is still
36+
possible to activate caching for select page elements by setting the `caching` property of their `@IdentifyUsing`
37+
annotation to `ON`.
38+
39+
> This approach will create the most overhead in execution time but will also stabilize your tests the most.
40+
41+
# Linked Documentation
42+
43+
- [Configuration](configuration.md)
44+
- [Page Objects](page-object.md)
11.1 KB
Loading

documentation/images/gs_tweet.png

10.5 KB
Loading

0 commit comments

Comments
 (0)