Why do you keep repeating yourself?

After you have been writing automated checks for a while, you tend to see similar patterns emerging. One of the most common bad patterns that you will see is tests that interact with the same page, and the same elements on that page, in different ways.

This normally happens because more than one person has been automating scenarios in the same area, and as with all things, different people have different ways of doing things.

Let's take as an example a couple of basic HTML pages. First of all, we will have our index page. This is the page that everybody is going to see when they navigate to our website:

<!DOCTYPE html>
<html lang="en">
<head>
<title>Some generic website</title>
<link href="http://cdnjs.cloudflare.com/ajax/libs/twitter-
bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet">
<link href="../css/custom.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top"
role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">
<img src="http://placehold.it/150x50&text=Logo"
alt="">
</a>
</div>
<div id="navbar-links">
<ul class="nav navbar-nav">
<li><a href="services.html">Services</a></li>
<li><a href="contact.html">Contact</a></li>
</ul>
</div>
</div>
</nav>
<div class="container">
<div class="row">
<div class="col-md-8">
<img class="img-responsive img-rounded"
src="http://placehold.it/900x350" alt="">
</div>
<div class="col-md-4">
<h1>Lorem ipsum dolor</h1>

<p>Duis in turpis finibus, eleifend nisl et, accumsan
dolor. Pellentesque sed ex fringilla,
gravida tellus in, tempus libero. Maecenas mi urna,
fermentum et sem vitae, congue pellentesque velit.</p>
<a class="btn btn-primary btn-lg" href="#">
Nam mattis</a>
</div>
</div>
<hr>
<footer>
<div class="row">
<div class="col-lg-12 left-footer">
<a href="about.html">About</a>
</div>
<div class="col-lg-12 right-footer">
<p>Copyright &copy; Your Website 2015</p>
</div>
</div>
</footer>
</div>
</body>
</html>

Our index page will look like this in a browser:

Next, we will have our about page. This page is going to tell the user a bit about the company history because people are interested in that sort of thing:

<!DOCTYPE html>
<html lang="en">
<head>
<title>Some generic website - About us</title>
<link href="http://cdnjs.cloudflare.com/ajax/libs/twitter-
bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet">
<link href="../css/custom.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top"
role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">
<img src="http://placehold.it/150x50&text=Logo"
alt="">
</a>
</div>
<div id="navbar-links">
<ul class="nav navbar-nav">
<ul class="nav navbar-nav">
<li><a href="services.html">Services</a></li>
<li><a href="contact.html">Contact</a></li>
</ul>
</ul>
</div>
</div>
</nav>
<div class="container">
<div class="row">
<div class="col-md-4">
<h1>About us!</h1>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing
elit. In nec elit feugiat, egestas tortor vel,
pharetra tellus. Mauris auctor purus sed mi finibus,
at feugiat enim commodo. Nunc sed eros nec libero
aliquam varius non vel sapien. Cras et nulla non
purus auctor tincidunt.</p>
</div>
</div>
<hr>
<footer>
<div class="row">
<div class="col-lg-12 left-footer">
<a href="about.html">About</a>
</div>
<div class="col-lg-12 right-footer">
<p>Copyright &copy; Your Website 2014</p>
</div>
</div>
</footer>
</div>
</body>
</html>

Our about page will look like this in a browser:

Both of these pages use a common custom.css that contains the following code:

body {
padding-top: 70px;
}

.navbar-fixed-top .nav {
padding: 15px 0;
}

.navbar-fixed-top .navbar-brand {
padding: 0 15px;
}

footer {
padding: 30px 0;
}

.left-footer {
float: left;
width: 20%;
}

.right-footer {
text-align: right;
float: right;
width: 50%;
}

@media (min-width: 768px) {
body {
padding-top: 100px
}

.navbar-fixed-top .navbar-brand {
padding: 15px 0;
}
}

There are probably many more pages that make up this site, but we are only interested in these two for our example.  

We have already put together a basic test framework in Chapter 1Creating a Fast Feedback Loop and Chapter 2, Producing the Right Feedback When Failing. To make things easy, we are going to write our tests using that framework.  

To make the code look more familiar in the tests below we are going to add a private variable called driver to our test classes which will hold our RemoteWebDriver object.  We will then use a @BeforeMethod to assign our RemoteWebDriver object to this private variable.  You don't have to do this if you don't want to, you can instead replace all instances of driver with getDriver().

private WebDriver driver;

@BeforeMethod
public void setup() throws MalformedURLException {
driver = getDriver();
}

Before we start writing any code, let's modify our POM.xml and add in an assertion library; after all, tests without assertions are not really tests. The assertion library that we are going to use is called AssertJ. It's a fluent assertion library that provides concise feedback in the console when things go wrong. Since it is a fluent API, it's also quite easy to find available assertion methods using your IDE's code autocompletion functionality. If you want to find out a bit more about AssertJ, have a look at the AssertJ web page: http://joel-costigliola.github.io/assertj/.  To bring this library in, we need to add the following dependency to our POM file:

<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.10.0</version>
<scope>test</scope>
</dependency>

Now that we have an assertion library in place, let's go back to the website. We have a couple of test scripts that have already been written for us and use our framework. This task was performed by a team of people in an attempt to get the work done really quickly. As a result, we had two different people working in isolation, on scripts that interact with these pages. 

We will call these scripts goToTheAboutPage() and checkThatAboutPageHasText():

@Test
public void goToTheAboutPage() {
driver.get("http://web.masteringselenium.com/index.html");
driver.findElement(By.cssSelector(".left-footer > a")).click();
WebElement element = driver.findElement(By.cssSelector("h1"));

assertThat(element.getText()).isEqualTo("About us!");
}

@Test
public void checkThatAboutPageHasText() {
driver.get("http://web.masteringselenium.com/index.html");
driver.findElement(By.cssSelector("footer div:nth-child(1)
> a"
)).click();
String titleText = driver.findElement(By.cssSelector
(".container > div h1")).getText();

assertThat(titleText).isEqualTo("About us!");
}

If you look closely, you can see that, while the scripts initially look different, they are actually doing the same thing. Which one is correct? Well, they both are! They are both reasonably sensible; they just use slightly different locator strategies. In fact, the reason that the second one was written is probably because the person writing the second one didn't realize that what they were writing had already been written.

So how could we get around this problem? How about making it clearer to the person reading the code what each locator is by giving it a sensible name?

Let's refactor goToTheAboutPage() a bit and see if we can make it easier to read:

@Test
public void goToTheAboutPage() {
driver.get("http://web.masteringselenium.com/index.html");

WebElement aboutLink = driver.findElement(By.cssSelector
(".left-footer > a"));

aboutLink.click();

WebElement aboutHeading =
driver.findElement(By.cssSelector("h1"));

assertThat(aboutHeading.getText()).isEqualTo("About us!");
}

We have now made goToTheAboutPage() much clearer. Anybody adding scripts will probably be able to look at this script and realize that some of the locators that they want to use have already been defined. You would then hope that they would use the same locators to save time and ensure consistency. This does of course assume that the somebody else who starts writing scripts looks at the old ones, or the somebody who is coming back to this set of tests after a period of time remembers what they did before and checks to see whether they already have something usable. 

Wouldn't it be easier if we could make some explicit definitions for the elements we are interested in that everybody knows about in advance? That way people won't have to keep repeating work that has already been done.

Well, we can; we can use the Don't Repeat Yourself (DRY) principle. We can take some common code that is regularly reused and put it into a centralized location where it can be repeatedly called. In the Selenium world, these definitions are called page objects

Page objects are badly named; they do not specifically refer to a page. They actually refer to any group of related objects. In many cases this will be an entire page, but in other cases it may be a part of a page or a component that is reused across many pages. 

Remember: If your page objects are hundreds of lines of code in length, you should break them up into much smaller classes so that they become manageable chunks of code.

When people start using page objects, they often fall into a few traps. Let's have a look at some common pitfalls.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.210.166