...

понедельник, 26 мая 2014 г.

Тестирование через абстрактные методы в TestNG

Вступление




Вы всё ещё тестируете с помощью JUnit и не обращаете внимания на TestNG? Тогда мы идём к вам.

Одним из преимуществ TestNG является возможность создания тестовых массивов данных для одного или нескольких тестов. Но мало кто использует такое преимущество от @DataProvider как пустой набор тестовых данных. В чём оно выражается?


Допустим у нас есть некий тест testData(String value) и метод datas обеспечивающий DataProvider. Если datas вернёт нам массив из 3-х элементов, то testData выполнится 3 раза. Но если datas вернёт нам пустой массив, то testData не выполнится ни разу


Картинки





Давайте попробуем воспользоваться данной особенностью.

Каркас приложения для тестирования




Предположим у нас есть некое класс OrganizationLoader, который читает из некоего каталога XML файлы, где каждый XML представляет собой описание департамента с сотрудниками и прочитанные данные трансформируются в объект Organization. (commit)
Исходный код


public class OrganizationLoader {

private OrganizationLoader() {
}

public static Organization loader(File orgDirectory) {
if (orgDirectory == null || !orgDirectory.exists() || !orgDirectory.isDirectory()) {
return null;
}

final File[] files = orgDirectory.listFiles();
if (files == null || files.length < 1) {
return null;
}

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;
}
}



public class Organization {
private List<Department> departments;

public List<Department> getDepartments() {
return departments;
}

public void setDepartments(List<Department> departments) {
this.departments = departments;
}
}



@XStreamAlias("department")
public class Department {
@XStreamAlias("name")
private String name;
@XStreamAlias("employees")
private List<Employee> employees;

public List<Employee> getEmployees() {
return employees;
}

public void setEmployees(List<Employee> employees) {
this.employees = employees;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}



@XStreamAlias("employee")
public class Employee {
@XStreamAlias("lastName")
private String lastName;
@XStreamAlias("firstName")
private String firstName;
@XStreamAlias("middleName")
private String middleName;

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getMiddleName() {
return middleName;
}

public void setMiddleName(String middleName) {
this.middleName = middleName;
}
}







Исходные условия для тестов




Итак, что мы хотим протестировать?


  1. Количество обработанных департаментов

  2. Количество сотрудников в департаменте dep1

  3. Фамилию у первого сотрудника в департаменте dep0 для организации org0

  4. Фамилию у второго сотрудника в департаменте dep2 для организации org1


Простые тесты




Здесь мы напишем обычные тесты без использования абстракций, дабы потом было с чем сравнить commit
Исходный код


public class Organization0Test {
private Organization organization;

@DataProvider
public Object[][] dataEmployeeCount() {
return new Object[][]{{"dep1", 2}};
}

@DataProvider
public Object[][] dataEmployeeLastName() {
return new Object[][]{{"dep0", 0, "empLastName0_0"}};
}

@BeforeMethod
public void setUp() 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);
}

@Test
public void testDepartmentCount() throws Exception {
Assert.assertEquals(organization.getDepartments().size(), 2);
}

@Test(dependsOnMethods = {"testDepartmentCount"}, dataProvider = "dataEmployeeCount")
public void testEmployeesCount(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")
public void testEmployeeLastName(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);
}
}



public class Organization1Test {
private Organization organization;

@DataProvider
public Object[][] dataEmployeeCount() {
return new Object[][]{{"dep1", 2}};
}

@DataProvider
public Object[][] dataEmployeeLastName() {
return new Object[][]{{"dep2", 1, "empLastName2_1"}};
}

@BeforeMethod
public void setUp() 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);
}

@Test
public void testDepartmentCount() throws Exception {
Assert.assertEquals(organization.getDepartments().size(), 3);
}

@Test(dependsOnMethods = {"testDepartmentCount"}, dataProvider = "dataEmployeeCount")
public void testEmployeesCount(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")
public void testEmployeeLastName(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);
}
}







Но мы же не хотим для каждого тестового набора писать новый тест, практически полностью повторяя программный код? Поэтому переходим к следующему шагу.
Написание абстрактного тест-класса и имплементации тестов от него



Создадим абстрактный класс AbstractTests и вынесем в него все тестовые методы. Так же, создадим методы-заглушки для DataProvider. (commit)
Исходный код


abstract public class AbstractTests {
protected Organization organization;

abstract protected String getOrgName();

@DataProvider
public Object[][] dataDepartmentCount() {
return new Object[][]{};
}

@DataProvider
public Object[][] dataEmployeeCount() {
return new Object[][]{};
}

@DataProvider
public Object[][] dataEmployeeLastName() {
return new Object[][]{};
}

@BeforeMethod
public void setUp() 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")
public void testDepartmentCount(int count) throws Exception {
Assert.assertEquals(organization.getDepartments().size(), count);
}

@Test(dependsOnMethods = {"testDepartmentCount"}, dataProvider = "dataEmployeeCount")
public void testEmployeesCount(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")
public void testEmployeeLastName(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);
}

}







И перепишем классы Organization0Test и Organization1Test, прописав там только тестовые наборы данных:
Исходный код


public class Organization0Test extends AbstractTests {

@Override
protected String getOrgName() {
return "org0";
}

@DataProvider
@Override
public Object[][] dataDepartmentCount() {
return new Object[][]{{2}};
}

@DataProvider
@Override
public Object[][] dataEmployeeCount() {
return new Object[][]{{"dep1", 2}};
}

@DataProvider
@Override
public Object[][] dataEmployeeLastName() {
return new Object[][]{{"dep0", 0, "empLastName0_0"}};
}
}



public class Organization1Test extends AbstractTests {

@Override
protected String getOrgName() {
return "org1";
}

@DataProvider
@Override
public Object[][] dataEmployeeCount() {
return new Object[][]{{"dep1", 2}};
}

@DataProvider
@Override
public Object[][] dataEmployeeLastName() {
return new Object[][]{{"dep2", 1, "empLastName2_1"}};
}

}





Фича раз



Если тестовый метод не выполнился из-за отсутствующих данных в DataProvider, то он всё равно будет считаться выполненным для зависимых тестов.

Это можно увидеть в Organization1Test — тестовый метод testEmployeesCount зависит от testDepartmentCount, который, согласно отчетам, не выполняется, т.к. для него нет тестового набора данных.
Фича два



Тесты ничем не отличаются от прочих методов в Java и их так же можно переопределять, тем самым выстраивая «деревья» из различных тестов. Если читателям интересно, то могу выложить пример такого «дерева».
Нюанс



Если в абстрактном классе AbstractTests удалить DataProvider-заглушку, то в зависимости от последовательности выполнения тестов, могут не выполниться ни один или выполниться только часть. Ошибки при этом не будет — только предупреждение.

Пример в отдельной ветке — была удалёна заглушка dataDepartmentCount.
картинка


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий