Part 3: Scripting using TestNG…

A Test Suite is a collection of tests. In our last post Part 2: Scripting using TestNG…, if we check the TestNG results of our ‘FirstTestNScript’ script, we had no control over Test Suite or Test name. By default they are named as ‘Default suite’ and ‘Default test’.

TestNG Result

However with the use of testing.xml, not only we can provide names to our test suites and tests but also we can configure our test run, set test dependency, include or exclude any test, method, class or package and set priority etc. In this post we will go through the testng.xml file creation and also some of the ways to configure our ‘FirstTestNScript’ script.

  1. Right click on our Java Project ‘FrameworkDesign’ and click New -> Other. Below window appears and we need to select XML file inside XML folder. Click Next and name the file as ‘testng.xml‘. Click Finish to close the window.

    Select XML file
  2. Once after completing the above step, we can see that the ‘testing.xml’ file is added to our Java project. There will be two tabs: Design and Source. In the Source tab, copy the below code.
    <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
    <!-- Naming our Suite as OurFirstSuite -->
    <suite name="OurFirstSuite" verbose="1" >
    <!-- Naming our Test as Functional. We can have as many tests --> 
      < test name="Functional" >
      <!-- Add TestNG class for execution -->
        < classes >
          <!-- Note class name include package name also -->
           < class name="com.selenium.testcase.FirstTestNScript" />
        </classes >
      </test >
    </suite >
    
  3. If we go through the above code, we provided our suite name as ‘OurFirstSuite’ in the <suite> tag. This is followed by <test name=”Functional”> tag. We can have as many <test> tags but with different name. Inside the <test> tags, <classes> tags are placed. Notice that class name is a combination of package name and TestNG class.
  4. To execute our TestNG script using ‘testing.xml’, right click on ‘testing.xml’ file and click Run As -> TestNG Suite.

    Run As TestNG Suite
  5. After successful execution, below results are displayed. Test Suite is now named as ‘OurFirstSuite’ and test is named as ‘Functional’.

    TestNG Results
  6. What if we want to run only ‘secondTestCase’ test from our ‘FirstTestNScript’ script? We can configure ‘testng.xml’ to select only required ‘@Test’ methods. In the Source tab, copy the below code.
    <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
    <!-- Naming our Suite as OurFirstSuite -->  
    <suite name="OurFirstSuite" verbose="1" >
    <!-- Naming our Test as Functional. We can have as many tests --> 
      <test name="Functional" >
      <!-- Add TestNG class for execution -->  
        <classes>
          <!-- Note class name include package name also -->
           <class name="com.selenium.testcase.FirstTestNScript">
           <!-- Include selective tests -->
           <methods>
           <!-- Only Second Test Case will be executed -->
           <include name="secondTestCase">
           </include>
           </methods>
           </class>
        </classes>
      </test>
    </suite>
    
  7. In the above code we are configuring ‘testing.xml’ to consider only one ‘@Test’ method from our ‘FirstTestNScript’ script. For this we have to use <method> tag followed by <include> tag. However to exclude a particular ‘@Test’ method from our test, we can use <exclude> tag inside <method> tag. After successful execution, below results are displayed. Only the included ‘@Test’ method is executed.

    secondTestCase executed
  8. We can configure ‘testing.xml’ to consider packages instead of classes. This comes in handy when we are doing regression testing. In this case, TestNG will look at all the classes in the package and will retain only classes that have TestNG annotations. In the Source tab, copy the below code and try.
    <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
    <!-- Naming our Suite as OurFirstSuite -->
    <suite name="OurFirstSuite" verbose="1">
    <!-- Naming our Test as Functional. We can have as many tests -->
    	<test name="Regression">
    		<!-- Execute based on packages -->
    		<packages>
    	<!-- Specify package names instead of class names -->
    			<package name="com.selenium.testcase">
                              </package>
    		</packages>
    	</test>
    </suite>
    
  9. After successful execution, below results are displayed. Notice that there is one more TestNG script named ‘SampleTest’ added to the package ‘com.selenium.testcase’. TestNG executed both files. If we closely analyze the test results, under ‘Regression’ test, there are two TestNG scripts – ‘FirstTestNScript’ and ‘SampleTest’. Inside ‘FirstTestNScript’, ‘secondTestCase’ (1 time) and ‘firstTestCase’ (2 times) are executed. And inside ‘SampleTest’, ‘firstTest’ (2 times) and ‘secondTest’ (2 times) are executed. This is due to Data Provider set up done in ‘FirstTestNScript’ and ‘SampleTest’ is different.

    TestNG Results
  10. Using TestNG we can also execute scripts belonging to set of groups. Groups are specified in ‘testng.xml’ file and need to be mentioned inside annotations. However one important point to remember is that groups must be mentioned for all annotations for which ‘@Test’ is dependant. For example in our ‘FirstTestNScript’ script, ‘firstTestCase’ is depending on ‘@BeforeTest’ for setting system properties, ‘@BeforeMethod’ for initializing WebDriver and ‘@AfterMethod’ for closing the WebDriver. Hence groups must be mentioned in all these methods. We will make below changes in our ‘FirstTestNScript’ script.
    	
    package com.selenium.testcase;
    
    import org.testng.annotations.Test;
    import org.testng.annotations.BeforeMethod;
    import org.testng.annotations.AfterMethod;
    import org.testng.annotations.DataProvider;
    import org.testng.annotations.BeforeClass;
    import org.openqa.selenium.By;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.chrome.ChromeDriver;
    import org.testng.Assert;
    import org.testng.annotations.AfterClass;
    import org.testng.annotations.BeforeTest;
    import org.testng.annotations.AfterTest;
    import org.testng.annotations.BeforeSuite;
    import org.testng.annotations.AfterSuite;
    
    public class FirstTestNScript {
    
    	WebDriver driver;
    
            @Test(dataProvider = "loginDP", priority=2, groups = "login")
    	public void firstTestCase(String username, String password) {
    		System.out.println("This is our First Test Method");
    		// Enter username as demo
    		driver.findElement(By.name("userName")).sendKeys(username);
    		// Enter password as demo
    		driver.findElement(By.name("password")).sendKeys(password);
    		// Click Sign in button
    		driver.findElement(By.name("login")).click();
    		// Assert if Sign Off is displayed after logging in
    		Assert.assertTrue(driver.findElement(By.linkText("SIGN-OFF")).isDisplayed());
    		System.out.println("First Test Case Passed");
    	}
    
    	// Assign WebDriver
    	@BeforeMethod(groups = "login")
    	public void beforeMethod() {
    		System.out.println("This is Before Method");
    		// Assign WebDriver to ChromeDriver
    		driver = new ChromeDriver();
    		// Maximize window
    		driver.manage().window().maximize();
    		// Navigate to New Tours URL
    		driver.get("http://newtours.demoaut.com");
    	}
    
    	@AfterMethod(groups = "login")
    	public void afterMethod() {
    		System.out.println("This is After Method");
    		// Close and quit the driver to close the Browser
    		driver.close();
    		driver.quit();
    	}
    
    	// Setting System Properties
    	@BeforeTest(groups = "login")
    	public void beforeTest() {
    		System.out.println("This is Before Test");
    		// Setting System properties
    		System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + "/Chrome/chromedriver");
    	}
    }
    
    
  11. And we will update the ‘testng.xml’ file with below code.
    <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
    <!-- Naming our Suite as OurFirstSuite -->
    <suite name="OurFirstSuite" verbose="1">
    	<!-- Naming our Test as Functional. We can have as many tests -->
    	<test name="Functional">
    	<!-- Include required groups to execute -->
    		<groups>
    		<!-- group = login -->
    			<run>
    				<include name="login" />
    			</run>
    		</groups>
    		<!-- Include classes where groups are mentioned -->
    		<classes>
    			<class name="com.selenium.testcase.FirstTestNScript" />
    		</classes>
    	</test>
    </suite>
    
  12. After successful execution, below results are displayed. Notice that only ‘firstTestCase’ is executed inside ‘FirstTestNScript’ script as it placed under ‘login’ groups.

    Groups example
  13. We have learned passing parameters using Data Provider. There is another way of passing parameters and that is through ‘testing.xml’. The parameter values can be set at both suite and test level in the testng XML file and based on scope, parameters can be used in the TestNG script. However any parameter value defined at the test level will override the value of a parameter, with same name, if defined at suite level. We will make following changes in our ‘firstTestCase’ code. Notice that we removed Data Provider from our ‘firstTestCase’ code and used ‘@Parameters’ instead. No other changes to ‘FirstTestNScript’ class.
    	@Test(groups = "login")
    	@Parameters({"username","password"})
    	public void firstTestCase(String username, String password) {
    		System.out.println("This is our First Test Method");
    		// Enter username as demo
    		driver.findElement(By.name("userName")).sendKeys(username);
    		// Enter password as demo
    		driver.findElement(By.name("password")).sendKeys(password);
    		// Click Sign in button
    		driver.findElement(By.name("login")).click();
    		// Assert if Sign Off is displayed after logging in
    		Assert.assertTrue(driver.findElement(By.linkText("SIGN-OFF")).isDisplayed());
    		System.out.println("First Test Case Passed");
    	}
    
  14. If we add parameters at suite level, then they are accessible to all the methods. In below example parameters ‘username’ and ‘password’ are accessible to all methods in TestNG script using ‘@Parameters’ annotation.
    <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
    <!-- Naming our Suite as OurFirstSuite -->
    <suite name="OurFirstSuite" verbose="1">
    <!-- Adding parameters at suite level -->
    	<parameter name="username" value="demo"></parameter>
    	<parameter name="password" value="demo"></parameter>
          
          <!-- Naming our Test as Functional. We can have as many tests -->
    	<test name="Functional">
    		<!-- Include required groups to execute -->
    		<groups>
    			<!-- group = login -->
    			<run>
    				<include name="login" />
    			</run>
    		</groups>
    		<!-- Include classes where groups are mentioned -->
    		<classes>
    			<class name="com.selenium.testcase.FirstTestNScript" />
    		</classes>
    	</test>
    </suite>
    
  15. If we add parameters at test level, then they are accessible to only those methods that are in scope of test. In below example parameters ‘username’ and ‘password’ are accessible to only those methods that are in scope of <test name=”Functional”>.
    <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
    <!-- Naming our Suite as OurFirstSuite -->
    <suite name="OurFirstSuite" verbose="1">
    	<!-- Naming our Test as Functional. We can have as many tests -->
    	<test name="Functional">
    		<!-- Adding parameters at test level -->
    		<parameter name="username" value="demo"></parameter>
    		<parameter name="password" value="demo"></parameter>
    		<!-- Include required groups to execute -->
    		<groups>
    			<!-- group = login -->
    			<run>
    				<include name="login" />
    			</run>
    		</groups>
    		<!-- Include classes where groups are mentioned -->
    		<classes>
    			<class name="com.selenium.testcase.FirstTestNScript" />
    		</classes>
    	</test>
    </suite>
    
  16. TestNG also allow us to send Optional parameters. If we don’t mention parameters in ‘testing.xml’ then there is way to pass parameters to test method using ‘@Optional‘ annotation. In below example, TestNG uses the parameters passed from ‘@Optional‘ for the ‘firstTestCase’. This happened because TestNG was unable to find parameters named ‘username’ and ‘password’ in the ‘testing.xml’ file.
    	@Test(groups = "login")
    	@Parameters({"username","password"})
    	public void firstTestCase(@Optional("demo") String username, @Optional("demo") String password) {
    		System.out.println("This is our First Test Method");
    		// Enter username as demo
    		driver.findElement(By.name("userName")).sendKeys(username);
    		// Enter password as demo
    		driver.findElement(By.name("password")).sendKeys(password);
    		// Click Sign in button
    		driver.findElement(By.name("login")).click();
    		// Assert if Sign Off is displayed after logging in
    		Assert.assertTrue(driver.findElement(By.linkText("SIGN-OFF")).isDisplayed());
    		System.out.println("First Test Case Passed");
    	}
    
    <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
    <!-- Naming our Suite as OurFirstSuite -->
    <suite name="OurFirstSuite" verbose="1">
    	<!-- Naming our Test as Functional. We can have as many tests -->
    	<test name="Functional">
    		<!-- Include required groups to execute -->
    		<groups>
    			<!-- group = login -->
    			<run>
                               <include name="login" />
    			</run>
    		</groups>
    		<!-- Include classes where groups are mentioned -->
    		<classes>
    			<class name="com.selenium.testcase.FirstTestNScript" />
    		</classes>
    	</test>
    </suite>
    
  17. There is one more way of passing parameters from ‘testing.xml’ and that is by using ‘org.testng.ITestContext’. In the ‘testing.xml’ file, we will pass parameters for url and ‘Chrome Driver location.
    <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
    <!-- Naming our Suite as OurFirstSuite -->
    <suite name="OurFirstSuite" verbose="1">
    <!-- Passing url and chrome driver location -->
    <parameter name="url" value="http://newtours.demoaut.com"></parameter>
    <parameter name="chromeDriver" value="/Chrome/chromedriver"></parameter>
    	<!-- Naming our Test as Functional. We can have as many tests -->
    	<test name="Functional">
    		<!-- Adding parameters at test level -->
    		<parameter name="username" value="demo"></parameter>
    		<parameter name="password" value="demo"></parameter>
    		<!-- Include required groups to execute -->
    		<groups>
    			<!-- group = login -->
    			<run>
    				<include name="login" />
    			</run>
    		</groups>
    		<!-- Include classes where groups are mentioned -->
    		<classes>
    			<class name="com.selenium.testcase.FirstTestNScript" />
    		</classes>
    	</test>
    </suite>
    
  18. We will make below changes to our ‘FirstTestNScript’ script. We will use ITestContext to pass data from ‘testng.xml’ file to our script. Notice that we removed unwanted methods from our ‘FirstTestNScript’ script.
    package com.selenium.testcase;
    
    import org.testng.annotations.Test;
    import org.testng.annotations.BeforeMethod;
    import org.testng.annotations.BeforeTest;
    import org.testng.annotations.AfterMethod;
    import org.testng.annotations.DataProvider;
    import org.testng.annotations.Parameters;
    import org.openqa.selenium.By;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.chrome.ChromeDriver;
    import org.testng.Assert;
    import org.testng.ITestContext;
    
    
    public class FirstTestNScript {
    
    	WebDriver driver;
    	String url;
    	String chromeDriverLocation;
    
    	@Test(groups = "login")
    	@Parameters({"username","password"})
    	public void firstTestCase(String username, String password) {
    		System.out.println("This is our First Test Method");
    		// Enter username as demo
    		driver.findElement(By.name("userName")).sendKeys(username);
    		// Enter password as demo
    		driver.findElement(By.name("password")).sendKeys(password);
    		// Click Sign in button
    		driver.findElement(By.name("login")).click();
    		// Assert if Sign Off is displayed after logging in
    		Assert.assertTrue(driver.findElement(By.linkText("SIGN-OFF")).isDisplayed());
    		System.out.println("First Test Case Passed");
    	}
    
    	@Test(dataProvider = "registerDP", priority=1)
    	public void secondTestCase(String fname, String lname, String username, String pwd, String cpwd) {
    		System.out.println("This is Second Test Method");
    		// Click Register link in Home page
    		driver.findElement(By.linkText("REGISTER")).click();
    		// Enter first name as fname
    		driver.findElement(By.name("firstName")).sendKeys(fname);
    		// Enter last name as lname
    		driver.findElement(By.name("lastName")).sendKeys(lname);
    		// Enter username as user
    		driver.findElement(By.name("email")).sendKeys(username);
    		// Enter password as pwd
    		driver.findElement(By.name("password")).sendKeys(pwd);
    		// Enter confirm password as cpwd
    		driver.findElement(By.name("confirmPassword")).sendKeys(cpwd);
    		// Click Submit to complete registration process
    		driver.findElement(By.name("register")).click();
    		System.out.println("Second Test Case Passed");
    	}
    
    	// Assign WebDriver
    	@BeforeMethod(groups = "login")
    	public void beforeMethod(ITestContext context) {
    		System.out.println("This is Before Method");
    		url = context.getCurrentXmlTest().getParameter("url");
    		// Assign WebDriver to ChromeDriver
    		driver = new ChromeDriver();
    		// Maximize window
    		driver.manage().window().maximize();
    		// Navigate to New Tours URL
    		driver.get(url);
    	}
    
    	@AfterMethod(groups = "login")
    	public void afterMethod() {
    		System.out.println("This is After Method");
    		// Close and quit the driver to close the Browser
    		driver.close();
    		driver.quit();
    	}
    
    	// DataProvider method for login data
    	@DataProvider
    	public Object[][] loginDP() {
    		// username = demo, passowrd = demo
    		return new Object[][] { { "demo", "demo" }, { "demo", "demo" } };
    	}
    
    	// DataProvider method for registering
    	@DataProvider
    	public Object[][] registerDP() {
    		// first name = fname, last name = lname, username = user, password =
    		// pwd, confirm password = pwd
    		return new Object[][] { { "fname", "lname", "user", "pwd", "pwd" } };
    	}
    
    
    	// Setting System Properties
    	@BeforeTest(groups = "login")
    	public void beforeTest(ITestContext context) {
    		System.out.println("This is Before Test");
    		chromeDriverLocation = context.getCurrentXmlTest().getParameter("chromeDriver");
    		// Setting System properties
    		System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + chromeDriverLocation);
    	}
    }
    
  19. In the above code, we are using ITestContext object to pass parameters from ‘testng.xml’ file to our script. We declared two String variables ‘url’ and ‘chromeDriverLocation’ and later assigning the values in ‘@BeforeTest’ and ‘@BeforeMethod’ methods. After successful execution, below results are displayed. Notice that all parameters are passed correctly from ‘testng.xml’ file and only ‘firstTestCase’ is executed.

    TestNG Result

With this we came to an end of TestNG tutorial. The topic is huge and it requires a constant practice to master it. Hope you were able to follow most of it.

Happy Learning!