Практически каждый тестовый сценарий состоит из трех частей:
- pre-conditions
- scenario
- post-conditions
В мире программирования такие методы принято называть setUp, test, tearDown:
- метод setUp отвечает за подготовку данных или системы в определенное состояние;
- метод test отвечает непосредственно за выполнение тестовой логики;
- метод tearDown отвечает за очистку от тестовых данных или возвращение системы в изначальное состояние.
В контексте web приложений и языка программирования Java, это будет выглядеть следующим образом:
@Before
public void setup() {
//Login to the system
steps.login();
}
@Test
public void addNewMember() {
// Test Logic
steps.openAddNewMember();
steps.addMember(newMember);
asserts.thatEditUserPageOpened();
asserts.thatUserPresentInViewMembers(newMember.getName());
}
@AfterMethod
public void tearDown() {
//Removing created member
steps.removeMember(newMember.getName());
}
Все функции реализованы с использованием WebDriver API, то есть взаимодействуют с браузером напрямую.
Пример функции removeMember:
public void removeMember(String name) {
viewMembersPage.open();
viewMembersPage.clickDeleteMemberBy(name);
viewMembersPage.acceptConfirmation();
}
public void clickDeleteMemberBy(String name) {
driver.findElement(By.xpath(String.format(CELL_MEMBER_NAME +
LINK_DELETE_MEMBER, name))).click();
}
Это простой пример. Но представьте каким количеством действий может обладать функция tearDown() для более комплексной системы. Или когда эта функция встречается в каждом тесте. Все это значительно отразится на скорости выполнения тестов.
Самый простой выход из ситуации, который я применяю на своих проектах – это использование библиотеки HttpClient (библиотека доступна в дистрибутиве selenium). Это бесплатная Java библиотека, позволяющая формировать HTTP запросы, отправлять их на сервер и принимать ответы в виде Java объектов.
При помощи этой библиотеки возможно самостоятельно формировать необходимые запросы, чтобы взаимодействовать с тестируемой системой посредством обращения к серверу, минуя взаимодействие с браузером. Таким образом, наши функции будут отрабатывать быстрее.
Как это выглядит:
private static int deleteUser(String name) throws IOException {
HttpGet deleteUserRequest = new HttpGet("http://cells.org.ua/scrum-selenium/admin/pageDeleteMember.php?memberID=" + name);
HttpResponse response = getHttpClient().execute(deleteUserRequest);
return response.getStatusLine().getStatusCode();
}
private static DefaultHttpClient getHttpClient() {
if (httpClient == null) {
PoolingClientConnectionManager cm = new PoolingClientConnectionManager();
cm.setMaxTotal(100);
httpClient = new DefaultHttpClient(cm);
}
return httpClient;
}
Для тех случаев, когда в вашу систему нужно предварительно залогиниться, вызываем POST запрос на вход до вызова функции removeMember.
Пример POST запроса, функция login:
private static int login() throws IOException {
HttpPost loginRequest = new HttpPost("http://cells.org.ua/scrum-selenium/admin/pageHome.php");
List<NameValuePair> credentials = new ArrayList<NameValuePair>();
credentials.add(new BasicNameValuePair("username", "admin"));
credentials.add(new BasicNameValuePair("password", "admin"));
loginRequest.setEntity(new UrlEncodedFormEntity(credentials, Consts.UTF_8));
HttpResponse response = getHttpClient().execute(loginRequest);
return response.getStatusLine().getStatusCode();
}
Пример класса на GitHub.
Небольшие замеры
long start = System.nanoTime();
steps.removeMember(newMember.getName());
// HttpActions.removeMember(newMember.getName());
long elapsedTime = System.nanoTime() - start;
Среднее время выполнение с использованием WebDriver API ~ 1055 milliseconds.
Среднее время выполнение с использование HTTPClient ~ 360 milliseconds.
Заменить WebDriver на использование HttpClient рискованно, так как мы принимаем все риски, связанные с работой приложения в браузере. Потому все, что вызывается внутри функции test, лучше делать при помощи WebDriver API, так как мы эмулируем действия конечного пользователя. Подготовительную же или завершающую части можно отнести на HttpClient.
Как альтернативу можно использовать HtmlUnit, этот инструмент работает по похожему принципу, но обернут в WebDriver API. При этом стоит учитывать риски, которые ограничивают работу HtmlUnit, а именно работа с тяжелыми JavaScript приложениями. При помощи HttpClient мы сами формируем запросы на сервер, что дает нам больше гибкости и снижает вероятность головной боли.
Все примеры кода доступны на GitHub.
Не хочешь пропускать ничего интересного? Подпишись на ленту RSS или следи за нами в Twitter!
Пользуюсь бесплатной любой jsoup для подобных случаев, очень удобно
Спасибо за дополнение с fluent API и jersey.
Работа с токенами заслуживает отдельного топика, там и правда интересно. С куками не так сложно, но в даном приложении POST запроса было достаточно.
Целью статьи было показать самый банальный пример использования и дать рабочий шаблон, который можно самостоятельно дорабатывать.
C fluent API действительно лучше.
Про перекладку печенек/токенов (и далее по списку) лучше добавить для цельности. Хотя сама по себе работа с печеньками и всяческими хэдерами в тестах под webdriver это отдельная очень сочная тема (очень много лайфхаков там прячется).
С API суть в том, что повыбивав из разработчиков правильные API можно не только порадовать разработчиков потенциального клиента классными игрушками, но еще и сильно улучшить Testability продукта (ускорить тесты избавившись от всякого мусора, получить море новых возможностей вокруг легко тестируемых компонент и т.д. и т.п.).
Что касается jersey, то по мне так тот же requests питоний вполне ок как REST клиент. И по мне так потребность в jersey скорее вырастает из чудовищности HttpClient. Но мне кажется на этом месте я уже в языковой холивар скатываюсь. Сойдемся на том что я кэннот ту джава, у меня глаза от xml болят и что ответ на ряд вопросов сильно зависит от языка.
Coockie надо передавать по идее, хотя это зависит от способа авторизации. Выглядеть оно может куда лучше с новым fluent API.
Для REST приложений куда лучше использовать не голый HttpClient, а REST клиента, например jersey.
Как же страшно оно выглядит в Java-то. После нескольких лет в Python requests (http://docs.python-requests.org/en/latest/user/authentication/) особенно.
А такая авторизация действительно работает? По идее же надо потом еще в сессию auth-токен какой-нибудь передавать.
ЗЫ: А вообще таким образом милое дело какое-нибудь REST API приложения из теста или из фикстур дергать