Flexible tests with DataProviders and Strategies

TestNG DataProviders are a powerful way to parameterize test classes, allowing you to run a single test method many times with different inputs.

For example, say we’d like to assert that a group of web search engines have a ‘search box’ on their home page, but because they’re maintained by different companies, don’t have a common HTML locator. Here we use a DataProvider to maintain a list of URLs and their corresponding ‘search box’ IDs, go to the URL and assert the search box is visible.

@DataProvider(name = "searchBoxProvider")
public Object[][] searchBoxProvider() {
return new Object[][] {
{ "http://www.google.com", "lst-ib" },
{ "https://duckduckgo.com", "search_form_input_homepage"},
{ "https://www.dogpile.com/", "topSearchTextBox"},
{ "http://www.bing.com", "sb_form_q"}
};
}

@Test(dataProvider = "searchBoxProvider")
public void shouldHaveVisibleSearchBox(String url, String searchBoxId) {
WebDriver driver = new FirefoxDriver();
driver.get(url);
WebElement searchBox = driver.findElement(By.id(searchBoxId));
assertTrue(searchBox.isDisplayed());
}

However, let’s say that one day DuckDuckGo adds an ad to their main page that hides the ‘search box’ until you close it, but we still want to assert that the ‘search box’ is still there. We could add some complexity to our @Test method that clicks the ad, but only for duckduckgo.com, or we could add a second DataProvider and a second @Test that does the additional step. Both of these make the test awful to read, unfortunately.

@DataProvider(name = "searchBoxProvider")
public Object[][] searchBoxProvider() {
return new Object[][] {
{ "http://www.google.com", "lst-ib" },
{ "https://duckduckgo.com", "search_form_input_homepage"},
{ "https://www.dogpile.com/", "topSearchTextBox"},
{ "http://www.bing.com", "sb_form_q"}
};
}

@Test(dataProvider = "searchBoxProvider")
public void shouldHaveVisibleSearchBox(String url, String searchBoxId) {
WebDriver driver = new FirefoxDriver();
driver.get(url);

if (StringUtils.equals(url, "https://duckduckgo.com")) {
WebElement adCloseButton = driver.findElement(By.id('mainAdCloseButton'));
adCloseButton.click();
}

WebElement searchBox = driver.findElement(By.id(searchBoxId));
assertTrue(searchBox.isDisplayed());
}

Using Strategies

I’ve found that using DataProviders to provide assertion Strategies is a great way to solve this type of problem. Here we create interface SearchEngine, and implementing classes DefaultSearchEngine and DuckDuckGo to abstract the varying behavior away of preparing the page from the test method.

interface SearchEngine {
static void preparePage(WebDriver driver);
}

class DefaultSearchEngine implements SearchEngine {
static void preparePage(WebDriver driver) {
// do nothing
}
}

class DuckDuckGo implements SearchEngine {
static void preparePage(WebDriver driver) {
WebElement adCloseButton = driver.findElement(By.id('mainAdCloseButton'));
adCloseButton.click();
}
}

And then provide a SearchEngine to the test method using searchBoxProvider.

@DataProvider(name = "searchBoxProvider")
public Object[][] searchBoxProvider() {
return new Object[][] {
{ "http://www.google.com", "lst-ib" , new DefaultSearchEngine()},
{ "https://duckduckgo.com", "search_form_input_homepage", new DuckDuckGo()},
{ "https://www.dogpile.com/", "topSearchTextBox", new DefaultSearchEngine()},
{ "http://www.bing.com", "sb_form_q", new DefaultSearchEngine()}
};
}

@Test(dataProvider = "searchBoxProvider")
public void shouldHaveVisibleSearchBox(String url, String searchBoxId, SearchEngine searchEngine) {
WebDriver driver = new FirefoxDriver();
driver.get(url);

searchEngine.preparePage(driver);

WebElement searchBox = driver.findElement(By.id(searchBoxId));
assertTrue(searchBox.isDisplayed());
}

Originally published at zdwolfe.blogspot.com on October 27, 2015.

Like what you read? Give Zach Wolfe a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.