Тестування через абстрактні методи в 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.  
  3. Кількість працівників у департаменті dep1
  4.  
  5. Прізвище у першого співробітника в департаменті dep0 для організації org0
  6.  
  7. Прізвище у другого співробітника в департаменті dep2 для організації org1
  8.  
 
 

Прості тести

Тут ми напишемо звичайні тести без використання абстракцій, щоб потім було з чим порівняти 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
. картинка
    
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.