Back to Home

Testing through abstract classes in TestNG

Do you still test with JUnit and ignore TestNG? Then we go to you. One of the advantages of TestNG is the ability to create test arrays of data for ...

Testing through abstract classes in TestNG

  • Tutorial

Introduction


Are you still testing with JUnit and ignoring TestNG? Then we go to you.

One of the benefits of TestNG is the ability to create test arrays of data for one or more tests. But few take advantage of @DataProvider as an empty test data set. What is it expressed in?

Suppose we have a certain test testData(String value)and method datasthat provides a DataProvider. If it datasreturns an array of 3 elements to us, it testDatawill be executed 3 times. But if it datasreturns an empty array to us, it testDatawill never be executed
Pictures


Let's try to use this feature.

Testing application framework


Suppose we have a class OrganizationLoaderthat reads XML files from a certain directory, where each XML represents a description of the department with employees and the data read is transformed into an Organization object. ( commit )
Source
publicclassOrganizationLoader{
	privateOrganizationLoader(){
	}
	publicstatic Organization loader(File orgDirectory){
		if (orgDirectory == null || !orgDirectory.exists() || !orgDirectory.isDirectory()) {
			returnnull;
		}
		final File[] files = orgDirectory.listFiles();
		if (files == null || files.length < 1) {
			returnnull;
		}
		XStream xStream = new XStream();
		xStream.processAnnotations(Department.class);
		Organization organization = new Organization();
		List<Department> departments = new ArrayList<>();
		for (File file : files) {
			try {
				String xml = FileUtils.readFileToString(file, "UTF-8");
				Department department = (Department) xStream.fromXML(xml);
				departments.add(department);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		organization.setDepartments(departments);
		return organization;
	}
}
publicclassOrganization{
	private List<Department> departments;
	public List<Department> getDepartments(){
		return departments;
	}
	publicvoidsetDepartments(List<Department> departments){
		this.departments = departments;
	}
}
@XStreamAlias("department")
publicclassDepartment{
	@XStreamAlias("name")
	private String         name;
	@XStreamAlias("employees")
	private List<Employee> employees;
	public List<Employee> getEmployees(){
		return employees;
	}
	publicvoidsetEmployees(List<Employee> employees){
		this.employees = employees;
	}
	public String getName(){
		return name;
	}
	publicvoidsetName(String name){
		this.name = name;
	}
}
@XStreamAlias("employee")
publicclassEmployee{
	@XStreamAlias("lastName")
	private String lastName;
	@XStreamAlias("firstName")
	private String firstName;
	@XStreamAlias("middleName")
	private String middleName;
	public String getFirstName(){
		return firstName;
	}
	publicvoidsetFirstName(String firstName){
		this.firstName = firstName;
	}
	public String getLastName(){
		return lastName;
	}
	publicvoidsetLastName(String lastName){
		this.lastName = lastName;
	}
	public String getMiddleName(){
		return middleName;
	}
	publicvoidsetMiddleName(String middleName){
		this.middleName = middleName;
	}
}


Initial conditions for tests


So what do we want to test?
  1. The number of departments processed
  2. Number of employees in the department dep1
  3. Surname of the first employee in the dep0 department for organizing org0
  4. Surname of the second employee in the dep2 department for organizing org1


Simple tests


Here we will write ordinary tests without using abstractions, so that later there is something to compare commit
Source
publicclassOrganization0Test{
	private Organization organization;
	@DataProviderpublic Object[][] dataEmployeeCount() {
		returnnew Object[][]{{"dep1", 2}};
	}
	@DataProviderpublic Object[][] dataEmployeeLastName() {
		returnnew Object[][]{{"dep0", 0, "empLastName0_0"}};
	}
	@BeforeMethodpublicvoidsetUp()throws Exception {
		File fRoot = new File(".");
		File orgDir = new File(fRoot, "src/test/resources/org0");
		Assert.assertTrue(orgDir.exists());
		Assert.assertTrue(orgDir.isDirectory());
		organization = OrganizationLoader.loader(orgDir);
		Assert.assertNotNull(organization);
	}
	@TestpublicvoidtestDepartmentCount()throws Exception {
		Assert.assertEquals(organization.getDepartments().size(), 2);
	}
	@Test(dependsOnMethods = {"testDepartmentCount"}, dataProvider = "dataEmployeeCount")
	publicvoidtestEmployeesCount(String depName, int countEmployee)throws Exception {
		final List<Department> departments = organization.getDepartments();
		int i = 0;
		Department department;
		do {
			department = departments.get(i++);
		} while (!department.getName().equals(depName));
		Assert.assertEquals(department.getName(), depName);
		Assert.assertEquals(department.getEmployees().size(), countEmployee);
	}
	@Test(dependsOnMethods = {"testDepartmentCount"}, dataProvider = "dataEmployeeLastName")
	publicvoidtestEmployeeLastName(String depName, int employeeIndex, String lastName)throws Exception {
		final List<Department> departments = organization.getDepartments();
		int i = 0;
		Department department;
		do {
			department = departments.get(i++);
		} while (!department.getName().equals(depName));
		Assert.assertEquals(department.getName(), depName);
		Employee employee = department.getEmployees().get(employeeIndex);
		Assert.assertEquals(employee.getLastName(), lastName);
	}
}
publicclassOrganization1Test{
	private Organization organization;
	@DataProviderpublic Object[][] dataEmployeeCount() {
		returnnew Object[][]{{"dep1", 2}};
	}
	@DataProviderpublic Object[][] dataEmployeeLastName() {
		returnnew Object[][]{{"dep2", 1, "empLastName2_1"}};
	}
	@BeforeMethodpublicvoidsetUp()throws Exception {
		File fRoot = new File(".");
		File orgDir = new File(fRoot, "src/test/resources/org1");
		Assert.assertTrue(orgDir.exists());
		Assert.assertTrue(orgDir.isDirectory());
		organization = OrganizationLoader.loader(orgDir);
		Assert.assertNotNull(organization);
	}
	@TestpublicvoidtestDepartmentCount()throws Exception {
		Assert.assertEquals(organization.getDepartments().size(), 3);
	}
	@Test(dependsOnMethods = {"testDepartmentCount"}, dataProvider = "dataEmployeeCount")
	publicvoidtestEmployeesCount(String depName, int countEmployee)throws Exception {
		final List<Department> departments = organization.getDepartments();
		int i = 0;
		Department department;
		do {
			department = departments.get(i++);
		} while (!department.getName().equals(depName));
		Assert.assertEquals(department.getName(), depName);
		Assert.assertEquals(department.getEmployees().size(), countEmployee);
	}
	@Test(dependsOnMethods = {"testDepartmentCount"}, dataProvider = "dataEmployeeLastName")
	publicvoidtestEmployeeLastName(String depName, int employeeIndex, String lastName)throws Exception {
		final List<Department> departments = organization.getDepartments();
		int i = 0;
		Department department;
		do {
			department = departments.get(i++);
		} while (!department.getName().equals(depName));
		Assert.assertEquals(department.getName(), depName);
		Employee employee = department.getEmployees().get(employeeIndex);
		Assert.assertEquals(employee.getLastName(), lastName);
	}
}


But we don’t want to write a new test for each test suite, repeating the program code almost completely? Therefore, we move on to the next step.

Writing an abstract test class and implementing tests from it

Let's create an abstract class AbstractTests and put all the test methods into it. Also, create stub methods for the DataProvider. ( commit )
Source
abstractpublicclassAbstractTests{
	protected Organization organization;
	abstractprotected String getOrgName();
	@DataProviderpublic Object[][] dataDepartmentCount() {
		returnnew Object[][]{};
	}
	@DataProviderpublic Object[][] dataEmployeeCount() {
		returnnew Object[][]{};
	}
	@DataProviderpublic Object[][] dataEmployeeLastName() {
		returnnew Object[][]{};
	}
	@BeforeMethodpublicvoidsetUp()throws Exception {
		File fRoot = new File(".");
		File orgDir = new File(fRoot, "src/test/resources/" + getOrgName());
		Assert.assertTrue(orgDir.exists());
		Assert.assertTrue(orgDir.isDirectory());
		organization = OrganizationLoader.loader(orgDir);
		Assert.assertNotNull(organization);
	}
	@Test(dataProvider = "dataDepartmentCount")
	publicvoidtestDepartmentCount(int count)throws Exception {
		Assert.assertEquals(organization.getDepartments().size(), count);
	}
	@Test(dependsOnMethods = {"testDepartmentCount"}, dataProvider = "dataEmployeeCount")
	publicvoidtestEmployeesCount(String depName, int countEmployee)throws Exception {
		final List<Department> departments = organization.getDepartments();
		int i = 0;
		Department department;
		do {
			department = departments.get(i++);
		} while (!department.getName().equals(depName));
		Assert.assertEquals(department.getName(), depName);
		Assert.assertEquals(department.getEmployees().size(), countEmployee);
	}
	@Test(dependsOnMethods = {"testDepartmentCount"}, dataProvider = "dataEmployeeLastName")
	publicvoidtestEmployeeLastName(String depName, int employeeIndex, String lastName)throws Exception {
		final List<Department> departments = organization.getDepartments();
		int i = 0;
		Department department;
		do {
			department = departments.get(i++);
		} while (!department.getName().equals(depName));
		Assert.assertEquals(department.getName(), depName);
		Employee employee = department.getEmployees().get(employeeIndex);
		Assert.assertEquals(employee.getLastName(), lastName);
	}
}


And we rewrite the classes Organization0Testand Organization1Test, having registered there only test data sets:
Source
publicclassOrganization0TestextendsAbstractTests{
	@Overrideprotected String getOrgName(){
		return"org0";
	}
	@DataProvider@Overridepublic Object[][] dataDepartmentCount() {
		returnnew Object[][]{{2}};
	}
	@DataProvider@Overridepublic Object[][] dataEmployeeCount() {
		returnnew Object[][]{{"dep1", 2}};
	}
	@DataProvider@Overridepublic Object[][] dataEmployeeLastName() {
		returnnew Object[][]{{"dep0", 0, "empLastName0_0"}};
	}
}
publicclassOrganization1TestextendsAbstractTests{
	@Overrideprotected String getOrgName(){
		return"org1";
	}
	@DataProvider@Overridepublic Object[][] dataEmployeeCount() {
		returnnew Object[][]{{"dep1", 2}};
	}
	@DataProvider@Overridepublic Object[][] dataEmployeeLastName() {
		returnnew Object[][]{{"dep2", 1, "empLastName2_1"}};
	}
}



Feature once

If the test method fails due to missing data in the DataProvider, then it will still be considered completed for dependent tests.
This can be seen in Organization1Test- the test method testEmployeesCountdepends on testDepartmentCountwhich, according to reports, is not executed, because there is no test data set for it.

Feature two

Tests are no different from other methods in Java and they can also be redefined, thereby building "trees" from various tests. If readers are interested, then I can lay out an example of such a "tree".

Nuance

If you AbstractTestsremove the DataProvider stub in an abstract class , then depending on the sequence of tests, none may be executed or only part may be executed. There will be no error - just a warning.
Example in a separate branch - the stub has been removed dataDepartmentCount.
picture

Read Next