Introducing the Query object

To remove the issues we are experiencing here, we are going to start building our page objects using something designed to get around the problems noted earlier: the Query object.

First of all, you will need to add the following dependency to your POM.xml file:

<dependency>
<groupId>com.lazerycode.selenium</groupId>
<artifactId>query</artifactId>
<version>1.2.0</version>
<scope>test</scope>
</dependency>

Then we are going to create an abstract class called BasePage that all other pages will be able to inherit from. We are doing this because we are going to need to have access to a RemoteWebDriver object, and we don't want to add this code to every single page object:

package com.masteringselenium.query_page_objects;

import com.masteringselenium.DriverBase;
import org.openqa.selenium.remote.RemoteWebDriver;

import java.net.MalformedURLException;

public abstract class BasePage {

protected RemoteWebDriver driver;

public BasePage() {
try {
driver = DriverBase.getDriver();
} catch (MalformedURLException ignored) {
//This will be be thrown when the test starts
//if it cannot connect to a RemoteWebDriver Instance
}
}
}

You will notice that we are ignoring any MalformedURLExceptions that are thrown.  This is because those will be thrown when the test initially starts as well so we don't need to worry about them in our page objects.  We won't get as far as actually using them if we can't get a valid driver instance to start our test.  We now have access to the Query object in our tests.   Let's convert our LoginPage to use it:

package com.masteringselenium.query_page_objects;

import com.lazerycode.selenium.util.Query;
import org.openqa.selenium.By;
import org.openqa.selenium.remote.BrowserType;

public class LoginPage extends BasePage {

private Query usernameField = new Query(By.id("username"),
driver);
private Query passwordField = new Query(By.id("password"),
driver);
private Query loginButton = new Query(By.id("login"), driver);

public void logInWithUsernameAndPassword(String username,
String password) {
usernameField.findWebElement().sendKeys(username);
passwordField.findWebElement().sendKeys(password);
loginButton.findWebElement().click();
}
}

You can instantly see that there is much less code, but we have the same level of functionality. Using the object is a little more verbose because we specify that we want to find WebElement instead of directly using WebElement. With autocomplete in modern IDEs, it's really not noticeable. You may have also noticed that we have removed the constructor. The Query object is initialized by passing in a RemoteWebDriver object when we declare our Query object. Hang on a minute, where did that driver object come from? We haven't defined it in this class! All of our page objects now extend the BasePage abstract class so we inherit all of the objects that have been initialized there.

If you have anything else that's reused by your page objects on a regular basis, you can also put them inside the BasePage class. I tend to put in an explicit wait object that can be inherited by all page objects so that I don't have to keep defining it again and again. This would also be a good place to start adding some generic utility classes. Make sure your BasePage class doesn't grow too large though; you don't want to turn it into a god class by mistake.

Now that we are up-and-running with the Query object, let's have a look at what else we can do with it. Remember the WebDriverWait example we had with the PageFactory class; let’s convert it to use our Query object:

WebDriverWait wait = new WebDriverWait(driver, 15, 100);
wait.until(ExpectedConditions.visibilityOfElementLocated
(usernameField.locator()));

You'll see that now, instead of using the .findWebElement() command on the Query object, we are now using the .locator() command.  This returns a full By object that can be used in our expected condition. We no longer have to try and remember whether that locator was an XPath, an ID, or a CSS locator.

What else can we do? Well, since the Query object is holding the locator, it's able to return multiple types for us. As well as returning an individual WebElement, we can return a list of WebElement objects, or even a Select object. We can also return objects of type MobileElement, which is useful if you are using Appium. Finally, we can specify different locators for different driver types. So, if we have some strange markup shown in IE that's different from every other modern browser, we could do this:

loginButton.addAlternateLocator(BrowserType.IE, By.id("only_in_ie"));

If the current WebDriver object is detected as having BrowserType as IE, any calls to .findWebElement() or .locator() will automatically use the IE locator instead of the default one.

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

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