Для работы со страницей Thucydides предоставляет класс
PageObject
, от которого должны быть унаследованы все Ваши страницы в проекте. В качестве своеобразной фабрики страниц выступает класс Pages
, который играет роль кеша и содержит методы для доступа и создания страниц.
Как получить страницу
Для того, чтобы получить объект страницы внутри библиотеки шагов нужно обратиться к фабрике страниц и запросить необходимый Вам класс:
LoginPage page = getPages().get(LoginPage.class);
Этот метод сам по себе всегда будет создавать новый экземпляр страницы. Возвращаемая страница при этом помещается в кеш. Для того чтобы не создавать объект страницы по-новой, если Вы итак уже находитесь на ней, можно обратиться к кешу и попытаться получить доступ к странице из него:
LoginPage page = getPages().onSamePage().get(LoginPage.class);
Если последняя созданная страница совпадает по классу с запрашиваемой, то вернется текущий объект страницы, если нет, то создаться новый.
Третий способ получения страницы позволяет не только получить экземпляр страницы, но и проверить текущий URL в браузере:
LoginPage page = getPages().currentPageAt(LoginPage.class);
Проверка URL проходит основываясь на URL паттерне определенном в аннотации @At в PageObject:
@At("http://www.site.org/login") public class LoginPage extends PageObject { ... }
@At поддерживает также использование регулярных выражений и шаблонов, игнорирует наличие параметров в URL и позволяет задать несколько url-ов для сравнения. Например,
// #HOST любой адрес сервера // любые символы после index @At("#HOST/catalog/index.*") public class CatalogItemPage extends PageObject { ... }
// одна страница для создания и редактирования @At(urls = { "#HOST/catalog/item/create", "#HOST/catalog/item/edit.*" }) public class CatalogItemPage extends PageObject { ... }
// одна страница для создания и редактирования @At("#HOST/catalog/item/(create|edit.*)") public class CatalogItemPage extends PageObject { ... }
Если при проверке URL не совпадает с приведенным в аннотации, то Thucydides генерирует исключение WrongPageError
и тест завершается неудачей. Аннотация @At не является обязательной и может быть опущена, тогда проверка просто не будет выполняться.
Как открыть страницу
Для того, чтобы открыть страницу в браузере используется метод open()
.
Объекты страниц, как правило, предназначены для работы с определенными веб-страницами. При вызове метода open()
браузер будет открыт по URL-у, представленному в аннотации @DefaultUrl.
@DefaultUrl("http://www.site.org/login") public class LoginPage extends PageObject { ... }
Однако в целом, HOST-часть в @DefaultUrl всегда будет переопределяться свойством webdriver.base.url
. Это позволяет установить базовый URL повсеместно для всех Ваших тестов, и упрощает запуск тестов на различных серверах простым изменением значения этого свойства. Например, в примере выше, установка webdriver.base.url
в http://yandex.ru приведет к открытию страницы по адресу http://yandex.ru/login.
Пример метода в библиотеке шагов для получения объекта страницы и открытия URL только в том случае, если Вы находитесь на другой странице:
/** * Открыть страницу. Проверка текущей страницы: * если находимся на другой странице, * то открываем необходимую * * @param pageObjectClass - класс страницы PageObject * @return T extends {@link PageObject} */ public T open(final Class pageObjectClass) { T page = null; try { page = getPages().onSamePage().currentPageAt(pageObjectClass); } catch (WrongPageError e) { page = getPages().get(pageObjectClass); page.open(); } return page; }
Страницу также можно открывать с передачей параметров с помощью метода open(final String... parameterValues)
, параметры в URL должны быть представлены в виде {1},{2} и т.д.
@DefaultUrl("/catalog/item/edit/{1}") public class CatalogItemPage extends PageObject { ... }
Вызов происходит следующим образом:
catalogPage.open("123374");
Вы также можете определить имена URL-адресов, которые могут быть использованы для открытия веб-страницы:
@DefaultUrl("/catalog/item/{1}") @NamedUrls( { @NamedUrl(name = "create", url = "/catalog/item/create"), @NamedUrl(name = "edit", url = "/catalog/item/edit/{1}"), } ) public class CatalogItemPage extends PageObject { ... }
catalogPage.open("edit", new String[] {"123374"});
Если Вам нужно, чтобы страница выполняла какие-то действия во время загрузки, например, дожидалась появления какого-либо динамического элемента, используйте аннотацию @WhenPageOpens
. Методы в PageObject, помеченные этой аннотацией, будут вызваны (в произвольном порядке) после того как URL был открыт.
@DefaultUrl("/catalog/list}") public class CatalogListPage extends PageObject { @FindBy(id="items-list"); WebElement list; @WhenPageOpens public void waitUntilListLoaded() { element(list).waitUntilVisible(); } }
Добрый день,
а установить webdriver.base.url можно только через -Dwebdriver.base.url и @ManagedPages(defaultUrl = «…») ?
можно установить напрямую через System Properties:
Также обратите внимание на методы в классах Pages и PageObject:
Можно раскрыть тему конфигурирования сборки в jenkins (хочется использовать одну и ту же сборку, но на нескольких тестовых стендах), каким образом передать webdriver.base.url
webdriver.base.url передается в строке, где вы запускаете тесты на выполнение:
clean integration-test -U -Dwebdriver.base.url=URL
Думаю, что детально раскрою эту тему в следующей статье.
Татьяна, спасибо большое.
А Вы не могли бы сказать, в каком классе проекта Thucydides нужно писать System.setProperty(
ThucydidesSystemProperty.BASE_URL.getPropertyName(),
DEFAULT_BASE_URL);
И чему именно присваивается значение System Properties?
Или это значение устанавливается в pom-файле? Или даже в командной строке maven?
ThucydidesSystemProperty.BASE_URL = «webdriver.base.url».
setProperty(ThucydidesSystemProperty.BASE_URL ...
или
-Dwebdriver.base.url
в командной строке — это просто два способа сделать одно и тоже, установить домен для тестов.В случае использования setProperty, установку лучше выполнить еще до запуска тестов, где-нибудь в @BeforeClass.
Спасибо, всё заработало )
если не сработал System.setProperty(ThucydidesSystemProperty.BASE_URL
.getPropertyName(), NEW_URL);
можно так переписать:
pages.getConfiguration().getEnvironmentVariables()
.setProperty(ThucydidesSystemProperty.BASE_URL
.getPropertyName(), NEW_URL);
Не совсем понятно, как взаимосвязаны @At и @DefaultUrl? Взаимозаменяемы ли они? Могут ли быть одновременно? Если да, для чего это может быть нужно?
Эти две аннотации никак явно не взаимосвязаны и не взаимозаменяемы. @At проверяет находитесь ли Вы на нужной странице в момент вызова
getPages().currentPageAt(pageObjectClass)
. А @DefaultUrl используется для открытия url в браузере при вызове методаpage.open()
. Они могут как присутствовать на странице одновременно, так и обе отсутствовать. Связать их можно только в step-ах, например, перед открытием страницы по url проверить, может Вы уже находитесь на нужной странице, и тогда не загружать ее лишний раз. В статье есть код, в котором показано как это сделать.Татьяна, можете рассказать как прикрутить jdbc к Thucydides?
Никакой функциональности для этого Thucydides не предоставляет. В целом они никак не взаимосвязаны. Многое зависит от самой реализации работы с базой. Но независимо от того, что Вы используете (ORM или просто отдельный класс, который подключается к БД), всю работу с базой лучше вынести в отдельный класс ScenarioSteps, например, DBSteps, и осуществлять все выборки и запросы через него. Это нужно для того, чтобы Thucydides мог корректно обработать возникшие исключения. Если будете работать с базой напрямую из теста, то в случае возникновения исключения, Вы не увидите в отчете никакой информации. PageObject в свою очередь тоже не самое подходящее место для работы с БД. Поэтому на мой взгляд степы наиболее оптимальное решение для такой задачи.
Что касается прикручивания jdbc в общем, то возможно эта статья чем-то поможет.
Как можно получить имя теста находясь внутри класса ScenarioSteps?
Непосредственно с помощью Thucydides это сделать нельзя. Но можно использовать один из следующих вариантов:
1. Определить имя теста в методе ScenarioSteps самостоятельно по каким-либо признакам:
2. Использовать текущую сессию Thucydides для передачи имени теста.
В тестовом классе:
В методе ScenarioSteps:
А в чем может быть проблема, если ни один из способов установки base.url не работает? По итогу открывается страница с адресом из defaultUrl. Пробовала на thucydides версии 90 и 77
Спасибо
Установка хоста через setProperty(ThucydidesSystemProperty.BASE_URL …) или -Dwebdriver.base.url наиболее приоритетна и должна работать. В классе Pages есть метод
getDefaultBaseUrl
:Pages.defaultBaseUrl устанавливается в defaultUrl из аннотации @ManagedPages в момент инициализации. В методе видно, что defaultBaseUrl возвращается только если нет другого значения, переданного в SystemPropeties.
Pages в свою очередь устанавливает хост для конкретной страницы во время вызова методов get, getAt, currentPageAt и т.п.
Может не работать скорее всего по двум причинам: не правильно устанавливаете или делаете это не вовремя.
Таже проблема что и у El, только -Dwebdriver.base.url открывает отличную от
@ManagedPages(defaultUrl = «http://HOST:PORT/page/»)
Возможно не вовремя устанивниваю SystemPropeties? Он он также отрабатывает в указанном вами @BeforeClass:
@Managed()
public WebDriver webdriver;
@ManagedPages(defaultUrl = "http://HOST:PORT/page/")
public Pages pages;
@BeforeClass
public static void beforeClass() {
System.setProperty(ThucydidesSystemProperty.BASE_URL.getPropertyName(), "http://newHOST:newPORT/page");
ThucydidesSystemProperties currentSystemProperties = ThucydidesSystemProperties.getProperties();
currentSystemProperties.setValue(ThucydidesSystemProperty.BASE_URL, "http://newHOST:newPORT/page")
}
Подскажите в чем может быть проблема?
Последние версии Thucydides считывают SystemPropeties во время инициализации теста, пересохраняя их в собственные объекты, поэтому дальнейшие изменения properties ни к чему уже не приводят.
В каждом конкретном случае нужно искать что конкретно необходимо изменить, в Вашем случае:
Добрый день, Татьяна!
Спасибо огромное за Ваш предыдущий ответ по поводу формирования отчетов. Отчеты заработали и формируются.
Я хотела спросить у Вас по поводу Ajax-запросов. Столкнулась с тем, что рекомендуемого checkThat(элемент)IsVisible не достаточно. На странице с множеством Ajax-запросов при отправке сообщения появляется алерт, который Thucydides пытается нажать (я использую alert.accept) до того как он физически появляется. Задать на алерт какие-то локаторы (и соответственно прописать его как элемент) я не могу. Ранее в аналогичном тесте, который я запускала через JUnit+Eclipse, для ожидания всех Ajax-запросов я использовала метод:
private void waitForAllAjaxComplete() throws InterruptedException {
Boolean ajaxIsComplete = false;
for (int second = 0;; second++) {
int timeout = 30;
if (second >= timeout)
fail(«timeout»);
try {
if (getDriver() instanceof JavascriptExecutor) {
ajaxIsComplete = (Boolean) ((JavascriptExecutor) getDriver())
.executeScript(«return jQuery.active == 0;»);
} else {
fail(«JavascriptExecutor»);
}
if (ajaxIsComplete)
break;
} catch (Exception e) {
}
Thread.sleep(1000);
Я честно попыталась его «прикрутить» и к thucydides но не слишком успешно, вернее у меня ничего из этого не получилось 🙁
Может быть где-то существует описание, как в thucydides работать с ajax-запросами кроме как использовать метод …IsVivsible????
Добрый день
Вопрос с алертом в частном порядке решается следующим образом:
Что касается ожидания завершения ajax-запросов, то из Вашего кода можно сделать примерно следующее:
К сожалению, мне не на чем проверить его работоспособность, но этот пример может помочь Вам придумать как реализовать Вашу задачу.
В ближайшие дни постараюсь написать статью про ajax в Thucydides.
Буду с нетерпением ждать! А пока я решила проблему с алертом довольно топорным способом, но он единственный, который заработал (я попозже планирую подергать специалистов на работе, что они предложат и обязательно отпишу если появится что-то более элегантное)
public void acceptAlert() {
getDriver().findElement(By.linkText(«Отправить сообщение»)).click();
}
}
Тот вариант, что Вы предложили у меня тоже не заработал, к сожалению…
Добрый день!
Я хотела уточнить один момент. Можно ли при использовании аннотации @At в один тест включать несколько подтестов.
Например класс
Test_Funkcional {
@Managed (uniqueSession = true)
@ManagedPages (defaultUrl = "default/url/")
@Steps
[...]
@Test
public void funkcional1 (){
[...]
}
`@Test
public void funkcional2(){
[...]
}
@Test
public void funkcional3(){
[...]
}
}
Ранее, без использования аннотации @At все тесты проходили у меня в одном браузере, по очереди.
Теперь же, с аннотацией @At, успешно проходит только первый из тестов (учитывая, что они запускаются рандомно, то это может быть любой из 5 или 6 тестов, прописанных в классе) . Для всех остальных тестов выдается следующая ошибка:
I was looking for a page compatible with class Test_Funkcional.LoginPage
I was at the URL https://url/defauil/payments/history.bml
Т.е. Thucydides на первом тесте успешно заходит на первую страницу, переходит на следующую, с которой уже дальше никуда не может уйти. Ни в текущем тесте (если в ходе теста требуется перейти последовательно по трем-четырем страницам). И самое главное Thucydides не позволяет последующим тестам открывать нужные им страницы и выполнять заданные действия.
Это моя ошибка в работе с аннотацией, или она просто не работает если необходимо в рамках одного теста заходить более, чем на две страницы и не работает если несколько тестов запускаются последовательно с аннотацией (uniqueSession = true)?
Заранее большое спасибо!
Добрый день!
Аннотация @At может использоваться в сценариях любой сложности.
Каким образом Вы заходите на страницы и переходите по ним?
Татьяна, здравствуйте!
Извините, что вопрос не совсем по теме поста.
Я использую JBehave тесты в Thucydides. И возникает проблема: когда я пытаюсь сделать что-то с WebDriver до начала выполнения непосредственных шагов теста, поднимается лишний браузер. Например, если сделать класс AbstractPage и в нем делать driver.manage().window().maximize(). Или если использовать аннотацию @BeforeStories и там вызывать шаг из библиотеки шагов, то при выполнении @BeforeStories поднимается первый браузер, а при выполнении первого шага теста — второй.
Не подскажете, в чем может быть дело?
К сожалению, никогда не использовала JBehave, возможно кто-то из читателей подскажет
Татьяна, спасибо за ответ. Еще один момент мне немного непонятен. Почему в конце выполнения тестов maven пишет в консоли, что выполнено тестов больше, чем есть на самом деле? Например, у меня 3 stories, а пишется, что тестов выполнено 13. Он не может их корректно подсчитать?
Могу только предположить, что он считает количество тестовых методов, а не тестовых классов
Подскажите пожалуйста, как сделать так чтоб браузер запускался с конкретным профайлом?
Раньше (без фусидидиса), я делал, как описано в одной из Ваших статей, и был доволен:
public void setUp() throws Exception {
ProfilesIni allProfiles = new ProfilesIni();
// PROFILE_NAME — имя профиля без указания пути, например, «default»
FirefoxProfile profile = allProfiles.getProfile(«Vitali4ek»);
webdriver = new FirefoxDriver(profile);
А теперь, куда бы не приткнул, либо открывается без моего профиля, либо сначала открывается браузер с профилем, а затем без профиля. И во втором выполняется тест.
Заранее спасибо 🙂
Для Thucydides путь к профилю можно задать только через system properties, например, при запуске из командной строки
mvn test -Dwebdriver.firefox.profile=PATH_TO_PROFILE
или установить программно до запуска тестов:
System.setProperty("webdriver.firefox.profile", PATH_TO_PROFILE);
Большое спасибо!
Татьяна, спасибо за интересную статью!
У меня к вам небольшой вопрос:
Есть возможность получить обект pageObjectClass например путём
getPages().get(LoginPage.class)
А есть ли возможность получить этот объект, для текущей страницы во время прохождения теста?
к примеру
loginPage.open(); — после открытия логин страницы, вызвать метод аля
—> pageObjectClass = getPages().getCurrentPage()
который бы вернул LoginPage.class
loginPage.do_login(); — были введены логин, пароль, нажата кнопка логин, как результат — мы на home page
—> pageObjectClass = getPages().getCurrentPage()
который бы вернул HomePage.class
Надеюсь понятно описал суть
Заранее благодарен за ответ.
Добрый день, никакого стандартного метода для это не существует, получить класс текущей страницы динамически средствами Thucydides невозможно.
Можно написать такой метод поиска нужного класса самим, отталкиваясь от текущего url, но задача очень нетривиальная и сильно зависит от построения url сайта.
Спасибо Татьяна!
Я реализовал это методом кеширования текущей страницы, при каждом переходе на оную, но рассчитывал на более феншуйный подход:)
Татьяна, здравствуйте. Подскажите пожалуйста, можно ли как-нибудь ограничить время загрузки страницы? У меня есть параметрический тест, который открывает n страниц и выполняет на них действия. Проблема заключается в том, что одна из страниц грузится очень долго, можно считать бесконечно. Собственно именно для вычисления таких страниц и был создан этот тест. Собственно, он даже не содержит PageObject, всё происходит в шагах:
@Step
public void startSystem(String url) {
getDriver().manage().timeouts().pageLoadTimeout(10, TimeUnit.SECONDS);
getDriver().get(url);
}
Для дефолтного фаерфокса работает как нужно. Проблема возникает когда я начинаю использовать HTMLUnit, задаю я его в классе теста:
@Managed(uniqueSession = true, driver = "HTMLUnit")
public WebDriver driver;
он почему-то совершенно не реагирует на заданное максимальное время ожидания и не прекращает тест.
Есть, конечно, вариант ограничить время выполнения теста установив timeout теста:
@Test(timeout = 10000)
public void someTest() {...}
Но в этом случае, судя по отчету thucydides тест прогоняется n раз, где n — количество параметров, т.е. количество страниц.
Как быть?