Hopefully you already know the importance of using the Page Object Model to keep your code organised. To keep your code maintainable it’s a good practice to have page inherit from a single abstract “BasePage” class. This allows us to have one central place that defines general actions that our pages can perform, and allows our concrete pages to inherit those behaviours, such as:
- Finding elements
- Clicking elements
- Checking whether an element is present
- Waiting for an element to become present
- Filling in text fields
- Selecting items from select lists
But these are all things you can access through the WebDriver API itself right? Yes, but to help write more readable and maintainable code it’s good to limit how much we make direct calls to the WebDriver API. It’s better to have methods that you directly control, that will allow you to make broad changes really easily if they become necessary.
For example, if a new version of Firefox is released that for some reason breaks WebDriver’s ability to know whether a page is ready or not, it’s much easier to fix that in your code by altering your “findElement” method to first wait for that element to be present before trying to use it. This kind of event tends to be few and far between, but it saves a lot of time versus refactoring the hundreds of driver.findElement calls you’ve got littered throughout your code.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public abstract class BasePage { | |
private WebDriver driver; | |
public BasePage(WebDriver driver) { | |
this.driver = driver; | |
} | |
public WebElement findElement(By by) { | |
return driver.findElement(by); | |
} | |
public void clickElement(By by) { | |
findElement(by).click(); | |
} | |
} |
This is our abstract BasePage class, containing a couple of simple methods that allow us to make very quick alterations to every base action that we take in our framework.
When we make a concrete page object, we extend from BasePage:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class LoginPage extends BasePage { | |
public LoginPage(WebDriver driver) { | |
super(driver); | |
} | |
public void clickLoginButton() { | |
clickElement(By.id("loginBtn")); | |
} | |
} |
We now have a simple system for sharing a set of common actions between all of our page objects. If we have to change the way we do things down the line, it’s now a one or two line change, rather than hours of work.
When we want to interact with the page, we create an instance of LoginPage and pass in our WebDriver instance:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class LoginTest { | |
// Simplified JUnit test structure, don't write tests like this! | |
@Test | |
public void simpleLoginTest() { | |
WebDriver driver = new ChromeDriver(); | |
LoginPage loginPage = new LoginPage(driver); | |
loginPage.clickLoginButton(); | |
} | |
} |
Leave a Reply