Оригинал статьи на украинском языке смотрите здесь.
На сегодняшний день существует много различных статей, курсов, тренингов в области автоматизации тестирования, которые рассказывают, каким инструментом делать клики пользователя или как правильно организовывать и запускать тесты. Однако в каждом из них опускается такая важная деталь, как место хранения тестовых данных, а также способ их извлечения.
Итак, как вы догадались, я бы хотел поговорить о тестовых данных для автоматизированных тестов. Скажите, что вам хочется сделать, когда вы видите следующий код?
public void setFirstName(String name) {
WebElement edtFirstName = driver.findElement(By.id(“firstname”));
edtFirstName.clear();
edtFirstName.sendKeys(name);
}
public void setUserDetails(String firstName, String lastName, String email) {
WebElement edtFirstName = driver.findElement(By.id(“firstname”));
edtFirstName.clear();
edtFirstName.sendKeys(firstName);
WebElement edtLastName = driver.findElement(By.id(“lasttname”));
edtLastName.clear();
edtLastName.sendKeys(lastName);
WebElement edtEmail = driver.findElement(By.id(“email”));
edtEmail.clear();
edtEmail.sendKeys(email);
}
Кто-то, конечно, ответит вопросом на вопрос: а что это за язык вообще? Отношусь с уважением к людям, которым важны детали. Это Java. Не знаю, как вам, а мне хочется, во-первых, заменить первые 3 строки метода setUserDetails
вызовом метода setFirstName
, а во-вторых, вынести последовательность действий clear()
и sendKeys()
в отдельный метод. Тем, кто этого не захотел сделать, предлагаю почитать бестселлер умного дяди Мартина Фаулера “Рефакторинг”. Это делается с той целью, чтобы при изменении реализации метода setFirstName
мы автоматически внесли изменения и в метод setUserDetails
, а если обобщить, то чтобы делать любое изменение только в одном месте.
Теперь внимание вопрос! Почему бы нам не сделать то же самое и с тестовыми данными?
Вопрос риторический, не переживайте. Давайте подумаем, как это можно реализовать. Концепция должна быть такая же, как и для кода, а именно: выносить общие части в место общего доступа для тех, кому это нужно.
Я уверен, что привел вас к определенным размышлениям, которыми вы уже можете воспользоваться для вашего текущего проекта, но если вы используете подход Data-Driven в написании тестов, то для вас есть еще и готовый инструмент для реализации подобной оптимизации. Имя ему Grible (http://www.grible.org).
Часто люди (а особенно те, кто работал с QTP (или как его теперь называют UFT)), привыкли хранить данные в Excel файлах, также возможен вариант XML, JSON, CSV, или тупо текстовые файлы. Ну что же, давайте рассмотрим пример с тестом на логин форму. Функционал логин формы такой: если введены правильные логин и пароль, то кнопка “Log in” становится активной. Поэтому чтобы проверить эту функцию с помощью подхода Data-Driven, создадим, например, такой Excel файл:
http://www.mbarvinskyi.com/oldsite/img/my/pine/excel.png
и напишем такой тест:
public void loginValidation() {
loginPage = NavigationUtils.startWebApplication();
loginPage.enterCredentials(data.get("Login"), data.get("Password"));
boolean expectedState = Boolean.parseBoolean(data.get("LoginButtonEnabled"));
loginPage.verifyLoginButtonEnabled(expectedState);
}
где data
– это HashMap<String, String>
, которая держит в себе колекцию пар ключ-значение текущей итерации теста. Количество итераций традиционно автоматически определяется количеством строк значений в дата-файле.
Затем вы под кайфом легкого и непринужденного написания тестов в таком стиле порождаете их десятками, а то и сотнями, а сколько тестов, столько и файлов с данными. А теперь представьте ситуацию, что заказчик решает добавить одно маааааленькое поле на логин форму. Например, поле Company, которое является обязательным для входа в апликуху. И здесь у вас начинается тихонько дергаться левый глаз, потому как вы прекрасно понимаете, что страница логина используется в 99% тестов.
И здесь вам на помощь приходит Grible! Главная фишка его в том, что он реализует концепцию, которую я называю Reusable Data Storage (хранилище данных многократного использования). Выглядит оно так:
То есть в grible мы создаем хранилище (data storage) Users:
http://www.mbarvinskyi.com/oldsite/img/my/pine/storage1.png
И привязываем его три строки в файл данных нашего теста (наводя курсор на ячейку с номером строки из хранилища, нашему вниманию предстает вид этой строки в хранилище):
Теперь генерируем класс UserInfo средствами grible (More → Generate class):
И создав его в коде, делаем соответственные изменения в тесте:
public void loginValidation() {
loginPage = NavigationUtils.startWebApplication();
UserInfo user = DataStorage.getDescriptor(UserInfo.class, data.get("User"));
loginPage.enterCredentials(user);
boolean expectedState = Boolean.parseBoolean(data.get("LoginButtonEnabled"));
loginPage.verifyLoginButtonEnabled(expectedState);
}
Page (screen) object теперь принимает не n параметров типа String, а один параметр типа UserInfo. Часто бывает так, что в такие методы нужно передать 10, а то и 20 параметров. Создание так называемого дескриптора (в нашем случае UserInfo) решает проблему гибкости и читабельности вызова таких методов.
Итак, теперь если нам нужно добавить поле Company на страницу логин формы, мы просто добавляем столбец Company к нашему хранилищу Users:
http://www.mbarvinskyi.com/oldsite/img/my/pine/storage3.png
Снова генерируем класс дескриптора (More → Generate class) и добавляем обработку этого параметра в метод enterCredentials()
:
public void enterCredentials(UserInfo user) {
edtLogin = browser.findElement(By.id("login"));
edtLogin.sendKeys(user.getLogin());
edtCompany = browser.findElement(By.id("company"));
edtCompany.sendKeys(user.getCompany());
edtPassword = browser.findElement(By.id("pswd"));
edtPassword.sendKeys(user.getPassword());
}
Вот и все! Заметьте, что ни сам тест, ни файл данных я не трогал. Все гибко и все работает!
Прошу задавать вопросы и высказывать пожелания в комментариях или в личных сообщениях. Попробую на все ответить.
Всем хорошего тестирования!