Test Data Builder pattern

Creating test object using they default constructors is very annoying when we have complicated model and variety of different test cases to cover. We can use with help of Creation Class or Object Mother pattern.

With them we can simplify object creation by reducing number of constructors and replace them with creation methods Then we have something like this:

Person person = PersonCreator.createPerson();
Person person = PersonCreator.createPersonWithAccount(Account accont);
Person person = PersonCreator.createPersonWithNameAndAccount(String name, Account account);

But those classes can become cluttered with every variation of object creation method that we use in unit testing. Then its very hard to find right one and we are ending creating new ones for every slightly different object.

Test Data Builder pattern for the rescue.

The main point of the Test Data Builder pattern is to allow to create an object with specific part of his data – the data that are significant to the test. Those data creation should be visible in “given” part of test and avalible for assertion.
The rest of them for which test don’t care should not be visible in test because they distract from actual test case. For this part of object data the builder will provide safe defaults.

This way test using Test Data Builders beacomes more concise and readable:

Person person = new PersonBuilder().build();
Person person = new PersonBuilder().withName("Bob").withoutAccount().build();

Builder source code:

public class PersonBuilder extends Builder{
	private Person person;
	
	public PersonBuilder() {
		person = new Person();
	}
	
	public PersonBuilder withId(String id){
		person.setId(id);
		return this;
	}

	public PersonBuilder withName(String name){
		person.setName(name);
		return this;
	}
	
	public PersonBuilder with(Account account) {
		person.getAccounts().add(account);
		return this;
	}

	public PersonBuilder withoutAccount() {
		person.setAccounts(new ArrayList());
		return this;
	}

	public Person build(){
		return person;
	}
}

With builder we can create some basic predifined builder object to decrease duplication. They can be modified for need of test using “withXXX” and “withoutXXX” methods:

//sample person object fits most of the test cases
Person person = aPerson().build();
//can be easly modified if needed
Person person = aPerson().withoutAccount().build();

For better readability use short version of method names for example:
instead of:

aPerson().withAccount(anAccount().build()).build();

use:

aPerson().with(anAccount().build()).build(); 

Conclusion

For sure builder pattern can reduce duplications in your test code and improve readability. But it needs some additional coding to create builders structure.
If you have complicated model you can use on of automated tools to generate builders code:

For Idea there is refactoring named “replace-constructor-with-builder”. But it uses “set” instead of “with” prefix convention. For more fluent generator you can use
Eclipse: fluent-builder-generator
Idea: Builder Generator

Here you can find example of builder implementation github.com/michal-lipski/mongodb-spring-data/:

Page Object pattern with selenium WebDriver

End to end or acceptance testing is most dificult part of testing lifecycle. In most cases it involves some GUI testing. People tend to treat test code as some less importatnt part of system and pay more attention to quality an readability when the test code main priority is that should be easy to understand and clear.

Here i will shortly describe Page Object patter that keeps our GUI testing code clean and easy to maintain.

Source code to this article available at github:
https://github.com/michal-lipski/page-objects-webdriver

Page Object pattern

A Page Object models UI page as object within the test code. This reduces the amount of duplicated code and centralizes web page data.It helps a lot with refactoring when UI changes which tends to happen very often.

Main concepts of Page Objects:

  • the public methods of Page Object should  represent the services that the page offers
  • hide all the internal details of the page (locators,elements ids,Urls) in Page Object
  • expose only services that page offers as a public methods
  • don’t make assertions in Page Object
  • sevice methods should return other Page Objects to reflect application flow
  • make sure that corresponding web page is fully loaded when you return Page Object
  • model different services for different results of one action

Page Object representing login page with login functionality covered:

public class GitHubLoginPage extends GitHubPage {

    @Override
    public String getUrl() {
        return "/login";
    }
 
    public void login(String login, String password) {
	getDriver().findElement(By.id("login_field")).sendKeys(login);        
	getDriver().findElement(By.name("password")).sendKeys(login);   
        getDriver().findElement(By.name("commit")).click();
    }
}

We can see that the login service method code looks complicated because of direct use of string literals to find elements on page. If we use those elements in other methods probably this would lead to introducing some constants. But there is a better way to do this with Selenium PageFactory.

Selenium WebDriver and PageFactory

PageFactory helps to remove some boiler-plate code from your Page Objects. Instead of declaring String describing element location we use WebElements.
So instead of:

driver.findElement(By.id("login_field")).sendKeys(login);

we have:

loginField.sendKeys(login);

To connect web page element with Page Object element we use @FindBy annotation:

@FindBy(id="login_field")
WebElement loginField;

@FindBy(name="password")
WebElement passwordField;

@FindBy(className="error_box")
WebElement errorBox;

Using findBy annotation we can describe how selenium should search for our element on page. We can search by id, name, className, css, xpath expression and others.
Before we can use those elements we have to inintialize our Page Object class.

GitHubLoginPage page = PageFactory.initElements(driver, GitHubLoginPage.class);

Putting it all together

Page Object give us ability to build fluent calls reflecting user interaction with application. Its clear to read and reduces complexity of test method.

Here is final version of a Page Object representing github login page.
It extends base page which have common code for initialization of PageFactory and implementation of wait until page is loaded. Wait condition is based on getPageLoadCondition() implementation delivered by subclasses.

public class GitHubLoginPage extends GitHubPage {

	@FindBy(id = "login_field")
	WebElement loginField;

	@FindBy(name = "password")
	WebElement passwordField;

	@FindBy(name = "commit")
	WebElement commitButton;

	@FindBy(className = "error_box")
	WebElement errorBox;

        @Override
	protected ExpectedCondition getPageLoadCondition() {
		//condition for full page load 
                return ExpectedConditions.visibilityOf(loginField);
	}

	@Override
	public String getUrl() {
		return "/login";
	}

	public void login(String login, String password) {
		loginField.sendKeys(login);
		passwordField.sendKeys(password);
		commitButton.click();
	}

	public boolean isLoginError() {
		return errorBox.isDisplayed();
	}

	public String getErrorMessage() {
		return errorBox.getText();
	}
}

And test method:

@Test
public void should_not_login_with_wrong_credentials() {
	//given
	GitHubLoginPage loginPage = new GitHubHomePage().open().goToLoginPage();
	//when
	loginPage.login("user", "password");
	//then
	assertThat(loginPage.isLoginError()).isTrue();
}