Automation Testing Framework using Selenium WebDriver and Java : Day 3
Configuring TestNG test runner file and running tests using TestNG.xml file.
In the last article we were able to execute a single testcase, configured ThreadLocal with WebDriver. Today, we will be adding a testng.xml file to run our testcases.
Note : We will be taking CURA Healthcare Service as out application under test, instead of Parasoft Parabank application.
Page Classes - Page chaining :
I have added 4 page classes as below, LandingPage.java being the enztry class.
In the LandingPage class, we have a method which will navigate us to the Login Page, after performing certain action, in this case it is the "doClickMakeAppointmentBtn()" method.
public class LandingPage {
public WebDriver driver;
public ElementUtil util;
public LandingPage(WebDriver driver) {
this.driver = driver;
util = new ElementUtil(driver);
}
//private By locators:
private By makeAppointmentBtn = By.id("btn-make-appointment");
private By menu = By.id("menu-toggle");
private By loginLink = By.linkText("Login");
//public page actions
public LoginPage doClickMakeAppointmentBtn() {
util.doClick(makeAppointmentBtn);
return new LoginPage(driver);
}
}
Here, doClickMakeAppointmentBtn() returns a LoginPage() object. As there is no other page actions that returns something to be validated, I have not added a test class with respect to LandingPage class.
We could have started from LoginPage directly as well. I wanted to explain the page chaining concept with this. Now, let's see one more class - LoginPage :
public class LoginPage {
private WebDriver driver;
private ElementUtil util;
public LoginPage(WebDriver driver) {
this.driver = driver;
util = new ElementUtil(driver);
}
//private By locators:
private By loginText = By.xpath("//h2");
private By username = By.id("txt-username");
private By password = By.id("txt-password");
private By loginBtn = By.id("btn-login");
//public page actions:
public String getPageUrl() {
return util.getCurrentUrl();
}
public boolean loginTextExists() {
return util.isElementDisplayed(loginText);
}
public AppointmentPage doLogin(String userName, String pass) {
util.doSendKeys(username, userName);
util.doSendKeys(password, pass);
util.doClick(loginBtn);
return new AppointmentPage(driver);
}
}
In the LoginPage class, we have 3 methods, 2 of which returns a certain value to be validated in its respective test class. The 3rd method, "doLogin()" returns a new AppointmentPage object.
Test classes - method chaining :
Now, let's see LoginPageTest class :
public class LoginPageTest extends BaseTest{
@BeforeClass
public void loginPage_setup() {
loginPage = landingPage.doClickMakeAppointmentBtn();
}
@Test
public void getPageUrlTest() {
Assert.assertTrue(loginPage.getPageUrl().contains(FrameworkConstants.LOGIN_PAGE_URL));
}
@Test
public void loginTextExistsTest() {
Assert.assertTrue(loginPage.loginTextExists());
}
}
In the @BeforeClass annotation, we are initializing the loginPage object. As we add new page classes, we will be declaring them in BaseTest class as below :
public class BaseTest {
protected DriverManager driverManager;
protected WebDriver driver;
protected ConfigurationReader configReader;
protected Properties properties;
protected LandingPage landingPage;
protected LoginPage loginPage;
protected AppointmentPage appointmentPage;
protected AppointmentConfirmationPage appointmentConfirmPage;
@BeforeTest
public void setup() {
configReader = new ConfigurationReader();
properties = configReader.initProperties();
driverManager = new DriverManager();
driver = driverManager.initDriver(properties);
landingPage = new LandingPage(driver);
}
@AfterTest
public void teardown() {
//driver.quit();
}
}
We are only initializing the LandingPage class object, as it is our entry point. We are declaring the class object references as "protected", because we need to be able to access them within the same class, subclass.
loginPage = landingPage.doClickMakeAppointmentBtn(); //method chaining
The "doClickMakeAppointmentBtn()" already returns the LoginPage object. And before and of the @Test methods gets executed, the @BeforeClass method will be executed.
Similarly, as we add more and more page classes, we can do method chaining to initialize a page class object as needed.
Testng.xml file :
Instead of running testcases individually from test classes, we will add runner files or the testng.xml files under src/test/resources/testrunners folder :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Regression Suite" verbose="4" parallel="tests" thread-count="10">
<test name="Login Page Test">
<classes>
<class name="tests.LoginPageTest" />
</classes>
</test>
<test name="Appointment Page Test">
<classes>
<class name="tests.AppointmentPageTest" />
</classes>
</test>
<test name="Appointment Confirmation Page Test">
<classes>
<class name="tests.AppointmentConfirmationPageTest" />
</classes>
</test>
</suite>
verbose="4" means, 4 lines of output will be displayed on console.
parallel="tests" means, we expect out tests to run in parallel mode. To run our testcases seamlessly, we implemented ThreadLocal to WebDriver, every thread will have their copy of driver reference value.
thread-count="10" means, maximum of 10 threads will be responsible for executing out testcases. In this case, we have only few tests, so we don't need as many as 10 threads.
Under each <test> we can have multiple classes. Test names have to be unique.
Now, run these testcases-> Right click on testng.xml file -> Run as -> 1 TestNG Suite.
Here is GitHub repository for reference : https://github.com/debasmita-a/cura-herokuapp-automation-testing
In the next article, we will learn to set up git repository and push our code.