As a start, let’s look at a few simple automated tests for the following test cases:
open site → search for keyword → select a random product price → check that the product info page is displayed
open site → search for keyword → select a random product name → check that the product info page is displayed
open site → search for keyword → select a random product company → check that the company info page is displayed
@Test
public void selectingRandomPriceDisplaysProductInfoPageTest() {
HomePage homePage = new HomePage();
homePage.open();
assertTrue(homePage.isDisplayed());
ResultsPage resultsPage = homePage.search(keyword);
assertTrue(resultsPage.isDisplayed());
ProductInfoPage infoPage = resultsPage.selectRandomPrice();
assertTrue(infoPage.isDisplayed());
}
@Test
public void selectingRandomNameDisplaysProductInfoPageTest() {
HomePage homePage = new HomePage();
homePage.open();
assertTrue(homePage.isDisplayed());
ResultsPage resultsPage = homePage.search(keyword);
assertTrue(resultsPage.isDisplayed());
ProductInfoPage infoPage = resultsPage.selectRandomName();
assertTrue(infoPage.isDisplayed());
}
@Test
public void selectingRandomCompanyDisplaysCompanyInfoPageTest() {
HomePage homePage = new HomePage();
homePage.open();
assertTrue(homePage.isDisplayed());
ResultsPage resultsPage = homePage.search(keyword);
assertTrue(resultsPage.isDisplayed());
CompanyInfoPage infoPage = resultsPage.selectRandomCompany();
assertTrue(infoPage.isDisplayed());
}
The tests use 4 page classes:
HomePage
ResultsPage
ProductInfoPage
CompanyInfoPage.
ResultsPage.java is below:
public class ResultsPage {
FindBy(id = "price")
List<WebElement> priceLinks;
FindBy(id = "name")
List<WebElement> nameLinks;
FindBy(id = "company")
List<WebElement> companyLinks;
private Random random = new Random();
......................
public ProductInfoPage selectRandomPrice() {
int count = priceLinks.size();
int i = random.nextInt(count);
WebElement priceLink = priceLinks.get(i);
priceLink.click();
return new ProductInfoPage (driver);
}
public ProductInfoPage selectRandomName() {
int count = nameLinks.size();
int i = random.nextInt(count);
WebElement nameLink = nameLinks.get(i);
nameLink.click();
return new ProductInfoPage(driver);
}
public CompanyInfoPage selectRandomCompany() {
int count = companyLinks.size();
int i = random.nextInt(count);
WebElement companyLink = companyLinks.get(i);
companyLink.click();
return new CompanyInfoPage(driver);
}
}
The selectRandomPrice(), selectRandomName() and selectRandomCompany() methods do the same thing. They click on a random price or a random name or a random company.
Their code is identical.
How were they created?
Through copy and paste.
If the code remains as is as a result of copy and paste, this is not good.
The issue is that all these methods are just duplicating the same code.
This is something to avoid as much as possible because
the overall code is longer
each method uses the same code for the same purpose, selecting a random element from a list; if, in the future, we will want to modify how this selection is done, we will need to modify all 3 methods
One way of removing the code duplication is by creating a new method that selects a random element from a list of elements and returns it. selectRandomPrice(), selectRandomName(), selectRandomCompany() will use the new private method:
public class ResultsPage {
......................
public ProductInfoPage selectRandomPrice() {
getRandomElement(priceLinks).click();
return new ProductInfoPage(driver);
}
public ProductInfoPage selectRandomName() {
getRandomElement(nameLinks).click();
return new ProductInfoPage(driver);
}
public CompanyInfoPage selectRandomCompany() {
getRandomElement(companyLinks).click();
return new CompanyInfoPage(driver);
}
private WebElement getRandomElement(List<WebElement> elements) {
int count = elements.size();
int i = random.nextInt(count);
return elements.get(i);
}
}
In this case, if the algorithm for selecting the random element changes, only 1 method will have to be modified. The overall code is also shorter than before.
But we have another problem. getRandomElement() is not at all about ResultsPage so it should be moved somewhere else.
We create a CollectionUtils class and move the method to it, together with a similar method that works on lists of string values:
public class CollectionUtils {
private static Random random = new Random();
public static WebElement getRandomElement(List<WebElement> elements) {
int i = random.nextInt(elements.size());
return elements.get(i);
}
public static String getRandomValue(List<String> values) {
int i = random.nextInt(values.size());
return values.get(i);
}
}
ResultsPage.java becomes
public class ResultsPage {
......................
public DetailsPage selectRandomPrice() {
CollectionUtils.getRandomElement(priceLinks).click();
return new DetailsPage(driver);
}
public ProductInfoPage selectRandomName() {
CollectionUtils.getRandomElement(nameLinks).click();
return new ProductInfoPage(driver);
}
public CompanyInfoPage selectRandomCompany() {
CollectionUtils.getRandomElement(companyLinks).click();
return new CompanyInfoPage(driver);
}
}
This version is much better since we solved the code duplication issue and also moved the new methods to a separate class.
CollectionUtils can be improved further with the use of generics but we will leave this for some other time.
For now, let’s just agree that, while copying and pasting code is useful when writing the first version of code, this code should be refactored so that it has as little duplicated code as possible.