Тестирование через абстрактные методы в 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
return organization;
}
}
public class Organization {
private List
public List
public void setDepartments (List
public List
public void setEmployees (List
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; } } Исходные условия для тестов Итак, что мы хотим протестировать? Количество обработанных департаментов Количество сотрудников в департаменте dep1 Фамилию у первого сотрудника в департаменте dep0 для организации org0 Фамилию у второго сотрудника в департаменте 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
@Test (dependsOnMethods = {«testDepartmentCount»}, dataProvider = «dataEmployeeLastName»)
public void testEmployeeLastName (String depName, int employeeIndex, String lastName) throws Exception {
final List
@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
@Test (dependsOnMethods = {«testDepartmentCount»}, dataProvider = «dataEmployeeLastName»)
public void testEmployeeLastName (String depName, int employeeIndex, String lastName) throws Exception {
final List
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
@Test (dependsOnMethods = {«testDepartmentCount»}, dataProvider = «dataEmployeeLastName»)
public void testEmployeeLastName (String depName, int employeeIndex, String lastName) throws Exception {
final List
} И перепишем классы 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. картинка