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

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 )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?
- The number of departments processed
- Number of employees in the department dep1
- Surname of the first employee in the dep0 department for organizing org0
- 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
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 )
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: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.