|
| 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 | + |
| 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.8 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 | + |
| 23 | + |
| 24 | +Configure the Maven project to look like this and replace `${version.webtester}` and `${version.selenium}` with the versions 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.1-SNAPSHOT</version> |
| 36 | + <packaging>jar</packaging> |
| 37 | + |
| 38 | + <properties> |
| 39 | + <maven.compiler.source>1.8</maven.compiler.source> |
| 40 | + <maven.compiler.target>1.8</maven.compiler.target> |
| 41 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| 42 | + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> |
| 43 | + |
| 44 | + <!-- dependency versions --> |
| 45 | + <version.webtester>2.0-SNAPSHOT</version.webtester> |
| 46 | + <version.selenium>2.53.0</version.selenium> |
| 47 | + </properties> |
| 48 | + |
| 49 | + <dependencies> |
| 50 | + |
| 51 | + <!-- Webtester --> |
| 52 | + <dependency> |
| 53 | + <groupId>info.novatec.testit</groupId> |
| 54 | + <artifactId>webtester-core</artifactId> |
| 55 | + <version>${version.webtester}</version> |
| 56 | + </dependency> |
| 57 | + <dependency> |
| 58 | + <groupId>info.novatec.testit</groupId> |
| 59 | + <artifactId>webtester-support-firefox</artifactId> |
| 60 | + <version>${version.webtester}</version> |
| 61 | + </dependency> |
| 62 | + <dependency> |
| 63 | + <groupId>info.novatec.testit</groupId> |
| 64 | + <artifactId>webtester-support-junit4</artifactId> |
| 65 | + <version>${version.webtester}</version> |
| 66 | + </dependency> |
| 67 | + |
| 68 | + <!-- Selenium --> |
| 69 | + <dependency> |
| 70 | + <groupId>org.seleniumhq.selenium</groupId> |
| 71 | + <artifactId>selenium-support</artifactId> |
| 72 | + <version>${version.selenium}</version> |
| 73 | + </dependency> |
| 74 | + |
| 75 | + <dependency> |
| 76 | + <groupId>org.seleniumhq.selenium</groupId> |
| 77 | + <artifactId>selenium-firefox-driver</artifactId> |
| 78 | + <version>${version.selenium}</version> |
| 79 | + </dependency> |
| 80 | + |
| 81 | + <!-- Testing --> |
| 82 | + <dependency> |
| 83 | + <groupId>junit</groupId> |
| 84 | + <artifactId>junit</artifactId> |
| 85 | + <version>4.12</version> |
| 86 | + </dependency> |
| 87 | + |
| 88 | + <!-- Logging --> |
| 89 | + <dependency> |
| 90 | + <groupId>ch.qos.logback</groupId> |
| 91 | + <artifactId>logback-classic</artifactId> |
| 92 | + <version>1.1.3</version> |
| 93 | + </dependency> |
| 94 | + </dependencies> |
| 95 | + |
| 96 | +</project> |
| 97 | +``` |
| 98 | + |
| 99 | +Create a new file: `testit-webtester.properties` and set your twitter credentials: |
| 100 | +```properties |
| 101 | +twitter.username=INSERT_TWITTER_USERNAME |
| 102 | +twitter.password=INSERT_TWITTER_PASSWORD |
| 103 | +``` |
| 104 | + |
| 105 | +## Create PageObject for Login Page |
| 106 | +After you've set up the project it's time to start writing some code. |
| 107 | + |
| 108 | +To login into your Twitter account we need to setup a PageObject for the Login Page. Open the TwitterLogin |
| 109 | +class and have it extend the PageObject class. Now we need to find the respective HTML elements to interact |
| 110 | +with on the Twitter page. WebTester can identify Objects by id, class, xpath or css using Selenium. After inspecting |
| 111 | +the HTML source of the Twitter Login Page you'll need to find the Objects you can interact with. In this case the login |
| 112 | +button, the username field and the password field. We'll identify these two fields by CSS-id, while we'll use the CSS-classes-concatenation to get the login button. |
| 113 | + |
| 114 | +We need to make sure that we are on the correct page @PostConstruct of the PageObject. To achieve this the visibility of a PageObject is verified. In our case the login button is checked. |
| 115 | + |
| 116 | +For all the gory details, please view the source code below at line 16. |
| 117 | + |
| 118 | +For the login process we need the following methods: setUsername, setPassword, clickLogin, and login. Note that these |
| 119 | +methods return type must extend PageObject. It is a representation of the state of the page opened after the methods |
| 120 | +execution. |
| 121 | + |
| 122 | +The methods setUsername and setPassword return instances of TwitterLogin while clickLogin and login return instances |
| 123 | +of TwitterHome. |
| 124 | + |
| 125 | +```java |
| 126 | +package pageobjects; |
| 127 | + |
| 128 | + |
| 129 | +import info.novatec.testit.webtester.conditions.pagefragments.Visible; |
| 130 | +import info.novatec.testit.webtester.pagefragments.Button; |
| 131 | +import info.novatec.testit.webtester.pagefragments.PasswordField; |
| 132 | +import info.novatec.testit.webtester.pagefragments.TextField; |
| 133 | +import info.novatec.testit.webtester.pagefragments.annotations.IdentifyUsing; |
| 134 | +import info.novatec.testit.webtester.pagefragments.annotations.PostConstructMustBe; |
| 135 | +import info.novatec.testit.webtester.pagefragments.annotations.WaitUntil; |
| 136 | +import info.novatec.testit.webtester.pages.Page; |
| 137 | + |
| 138 | + |
| 139 | +public interface TwitterLogin extends Page { |
| 140 | + |
| 141 | + @PostConstructMustBe(Visible.class) |
| 142 | + @WaitUntil(Visible.class) |
| 143 | + @IdentifyUsing(".primary-btn.flex-table-btn.js-submit") |
| 144 | + Button loginButton(); |
| 145 | + @IdentifyUsing("#signin-email") |
| 146 | + TextField usernameField(); |
| 147 | + @IdentifyUsing("#signin-password") |
| 148 | + PasswordField passwordField(); |
| 149 | + |
| 150 | + default TwitterHome login(String username, String password) { |
| 151 | + return setUsername(username).setPassword(password).clickLogin(); |
| 152 | + } |
| 153 | + |
| 154 | + default TwitterLogin setUsername(String username) { |
| 155 | + usernameField().setText(username); |
| 156 | + return this; |
| 157 | + } |
| 158 | + |
| 159 | + default TwitterLogin setPassword(String password) { |
| 160 | + passwordField().setText(password); |
| 161 | + return this; |
| 162 | + } |
| 163 | + |
| 164 | + default TwitterHome clickLogin() { |
| 165 | + loginButton().click(); |
| 166 | + return create(TwitterHome.class); |
| 167 | + } |
| 168 | +} |
| 169 | +``` |
| 170 | + |
| 171 | +**Note:** When using `@IdentifyUsing` with an CSS-Selector, it's not necessary to explicitly define the method used as CSS is the default. |
| 172 | + |
| 173 | +## Create PageObject for Home Page |
| 174 | + |
| 175 | +Similar to the Login Page we create a PageObject representation for the Twitter Home Page. |
| 176 | + |
| 177 | +```java |
| 178 | +package pageobjects; |
| 179 | + |
| 180 | +import pageobjects.widgets.TwitterBox; |
| 181 | + |
| 182 | +import info.novatec.testit.webtester.conditions.pagefragments.Visible; |
| 183 | +import info.novatec.testit.webtester.pagefragments.Button; |
| 184 | +import info.novatec.testit.webtester.pagefragments.PageFragment; |
| 185 | +import info.novatec.testit.webtester.pagefragments.annotations.IdentifyUsing; |
| 186 | +import info.novatec.testit.webtester.pagefragments.annotations.PostConstructMustBe; |
| 187 | +import info.novatec.testit.webtester.pagefragments.identification.producers.XPath; |
| 188 | +import info.novatec.testit.webtester.pages.Page; |
| 189 | + |
| 190 | + |
| 191 | +public interface TwitterHome extends Page { |
| 192 | + |
| 193 | + @PostConstructMustBe(Visible.class) |
| 194 | + @IdentifyUsing("#tweet-box-home-timeline") |
| 195 | + TwitterBox tweetBox(); |
| 196 | + |
| 197 | + @IdentifyUsing(".btn.primary-btn.tweet-action.tweet-btn.js-tweet-btn") |
| 198 | + Button sendTweetButton(); |
| 199 | + |
| 200 | + @IdentifyUsing(value = ".//ol[@id='stream-items-id']/li[1]//p[contains(@class, 'tweet-text')]", how = XPath.class) |
| 201 | + PageFragment latestTweet(); |
| 202 | + |
| 203 | + default TwitterHome tweet(String message) { |
| 204 | + return setTweetMessage(message).sendTweet(); |
| 205 | + } |
| 206 | + |
| 207 | + default TwitterHome setTweetMessage(String message) { |
| 208 | + tweetBox().setMessage(message); |
| 209 | + return this; |
| 210 | + } |
| 211 | + |
| 212 | + default TwitterHome sendTweet() { |
| 213 | + sendTweetButton().click(); |
| 214 | + return create(TwitterHome.class); |
| 215 | + } |
| 216 | + |
| 217 | + default String getMessageOfLatestTweet() { |
| 218 | + return latestTweet().getVisibleText(); |
| 219 | + } |
| 220 | +} |
| 221 | +``` |
| 222 | + |
| 223 | +As PageObjects can represent either entire pages or just a certain part of it, we use the TweetBox class as a |
| 224 | +representation of Twitter´s TweetBox. |
| 225 | + |
| 226 | +```java |
| 227 | +package pageobjects.widgets; |
| 228 | + |
| 229 | +import info.novatec.testit.webtester.pagefragments.traits.Clickable; |
| 230 | + |
| 231 | + |
| 232 | +public interface TwitterBox extends Clickable { |
| 233 | + |
| 234 | + default TwitterBox setMessage(String message) { |
| 235 | + click(); |
| 236 | + webElement().sendKeys(message); |
| 237 | + return this; |
| 238 | + } |
| 239 | +} |
| 240 | +``` |
| 241 | + |
| 242 | +## Create the Test |
| 243 | + |
| 244 | +The `TwitterTest` makes use of JUnit's `@RunWith` annotation with the `WebTesterJUnitRunner` class for automated resource |
| 245 | +management. |
| 246 | + |
| 247 | +**In case your tests are already using another JUnit Runner, you'll have to initialize your ```Browser``` |
| 248 | +instances manually. See the [Browser](browser.md) documentation for details!** |
| 249 | + |
| 250 | +In this example the `@CreateUsing` annotation specifies the `Browser` to be an instance of Firefox. |
| 251 | + |
| 252 | +The `@EntryPoint` is the initial page used by WebTester. It´s the first page the configured browser navigates to. |
| 253 | + |
| 254 | +The Twitter password and username are taken from the `testit-webtester.properties` file using the `@ConfigurationValue` |
| 255 | +annotation. |
| 256 | + |
| 257 | +`@Before` test execution a representational instance of the Twitter Login page (twitter.com) is returned by the |
| 258 | +`Browser` as a `TwitterLogin` (created above) object. |
| 259 | + |
| 260 | +During the `@Test` WebTester will use twitterLogin to log in with the username and password specified in the |
| 261 | +properties file. After a successful login a tweet containing the current system time is tweeted and the test then |
| 262 | +asserts that the tweet has been tweeted. In this case the assertion happens implicit via a Wait. As there is a delay in Ajax calls we have to wait until |
| 263 | +the tweet is actually posted. The condition which verifies the waiting time also acts as assertion, that the right tweet happened. |
| 264 | + |
| 265 | +```java |
| 266 | +package test; |
| 267 | + |
| 268 | +import java.util.Date; |
| 269 | +import javax.annotation.Resource; |
| 270 | + |
| 271 | +import org.junit.Before; |
| 272 | +import org.junit.Test; |
| 273 | +import org.junit.runner.RunWith; |
| 274 | + |
| 275 | +import pageobjects.TwitterHome; |
| 276 | +import pageobjects.TwitterLogin; |
| 277 | + |
| 278 | +import info.novatec.testit.webtester.browser.Browser; |
| 279 | +import info.novatec.testit.webtester.browser.proxy.FirefoxFactory; |
| 280 | +import info.novatec.testit.webtester.conditions.Conditions; |
| 281 | +import info.novatec.testit.webtester.junit.annotations.ConfigurationValue; |
| 282 | +import info.novatec.testit.webtester.junit.annotations.CreateUsing; |
| 283 | +import info.novatec.testit.webtester.junit.annotations.EntryPoint; |
| 284 | +import info.novatec.testit.webtester.junit.runner.WebTesterJUnitRunner; |
| 285 | +import info.novatec.testit.webtester.waiting.Wait; |
| 286 | + |
| 287 | + |
| 288 | +@RunWith(WebTesterJUnitRunner.class) |
| 289 | +public class TwitterTest { |
| 290 | + |
| 291 | + @Resource |
| 292 | + @CreateUsing(FirefoxFactory.class) |
| 293 | + @EntryPoint("https://twitter.com/?lang=en") |
| 294 | + private Browser browser; |
| 295 | + |
| 296 | + @ConfigurationValue("twitter.username") |
| 297 | + private String username; |
| 298 | + @ConfigurationValue("twitter.password") |
| 299 | + private String password; |
| 300 | + |
| 301 | + private TwitterLogin twitterLogin; |
| 302 | + |
| 303 | + @Before |
| 304 | + public void initStartPage() { |
| 305 | + twitterLogin = browser.create(TwitterLogin.class); |
| 306 | + } |
| 307 | + |
| 308 | + @Test |
| 309 | + public void testThatMessageCanBeTweeted() throws InterruptedException { |
| 310 | + |
| 311 | + String tweetMessage = "Hello World on: " + new Date(); |
| 312 | + TwitterHome home = twitterLogin.login(username, password).tweet(tweetMessage); |
| 313 | + |
| 314 | + Wait.until(home.latestTweet()).is(Conditions.visibleText(tweetMessage)); |
| 315 | + } |
| 316 | +} |
| 317 | +``` |
| 318 | + |
| 319 | +## Summary |
| 320 | +Congratulations! You´ve just developed a simple Twitter login and tweet test with WebTester. |
0 commit comments