Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- public class GmailPage {
- /*
- с инициализацией пейджа - все ок
- */
- // refresh the page
- @FindBy(xpath = "//div[contains(@class, 'asf')]")
- WebElement refresh;
- /*
- тут через css - можно чуть проще переписать
- ну то такое - мелочь
- погоду в данном случае не сделает
- */
- // locator of first found letter
- @FindBy(css = ".srg .g:nth-child(1)")
- public WebElement firstResult;
- /*
- а вот это - с другой оперы ) - из google search
- в любом случае
- у тебя выше есть emails
- и нам его хватит - если говорить о работе со списком мейлов
- */
- public void setLogin(String email, String password) {
- loginEmail.sendKeys(email, Keys.ENTER);
- loginPassword.sendKeys(password, Keys.ENTER);
- }
- /*
- в чем особенность селениумских прокси элементов/списков (те, что @FindBy аннотированы)
- да, они переискиваются при обращении к ним
- НО - не ждут)
- переискали, и даже если что-то там еще недогрузилось - вернули нам то, что есть на этот момент)
- так что - неудивительно, что тормознутый GMail давал тебе такого рода ошибки)
- когда мы можем кликнуть, даблкликнуть и т д
- когда элемент - уже видим
- значит - нам нужно этого дождаться
- поскольку - это частое сочетание - дождаться видимости элемента + что-то там с ним сделать
- реализуй метод WebElement $(WebDriver driver, WebElement proxyElement)
- в котором - сначала - дожидайся видимости элемента
- затем - возвращай его же
- поскольку метод - универсальный
- расположи его в классе-контейнере универсальных методов (пусть будет ConciseAPI, например)
- пока - в этой версии - вебдрайвер - передадим в метод как параметр
- и тогда - тут и далее - используй метод $
- $(driver, loginEmail).sendKeys(email, Keys.ENTER);
- и все ок будет)
- вот тебе еще один пример - когда в методе-действии
- мы используем ждущую проверку
- по сути - такая проверка - выполняет не функции собственно тестовой проверки
- а функции умного ожидания - нам нужно дождаться верной тестовой ситуации
- перед тем как что-то выполнить с элементом - нам нужна его видимость
- этот момент учти для всех прокси веб-элементов
- полезные линки
- https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html
- http://selenium2advanced.blogspot.com/2013/08/working-with-page-factory-and-webdriver.html
- https://github.com/SeleniumHQ/selenium/wiki/PageFactory
- на самом деле - это последняя работа - где мы используем @FindBy элементы и списки
- советую разобраться с этим тут - на случай - вдруг пригодится
- а вообще - не самая замечательно реализованная вещь)
- но для разбора - как @FindBy работает и с чем его едят - полезно разочек поработать
- для пейджа Selenium-решения -
- вариант без @FindBy - тоже предпочтительнее
- вот почему:
- - гораздо менее лаконичный и менее читабельный
- - магичный и не ооп-шный - не консистентный со всем другим кодом создания прокси элементов, и вообще классов в проекте -
- а значит ньюкамерам сложнее въехать
- - Page Factory в принципе зло.
- Этого Яков касается в докладе https://www.youtube.com/watch?v=Hi6EYEkSvrk
- Page Factory вся наша тусовка кстати - считает злом.
- И Пирогов
- И Солнцев
- и главное - Баранцев (коммиттер в селениум) .
- Есть планы ее выкинуть с Селениума вообще
- */
- **************************
- public void assertTrue(ExpectedCondition<Boolean> condition) {
- (new WebDriverWait(driver, 10)).until(condition);
- }
- /*
- Это метод - точно не метод пейджа
- вынеси его в ConciseAPI тоже
- да и тебе не всегда будет нужен вариант работы с кондишеном ExpectedCondition<Boolean>
- пока метод возвращает void - можно вообще не ограничивать себя - и работать с ExpectedCondition (не уточняя дженерик-типа)
- с дженерик-типом - конечно, тоже придется разобраться
- но это можно сделать и позже)
- по финишу - почитаешь
- если вопросов не будет - применишь
- если будет - ограничься пока версией метода без уточнения дженерик-типа для ExpectedCondition
- раз так - то корректнее будет назвать метод assertThat
- и еще - поскольку вынесем метод в ConciseAPI - нужно в метод передавать и вебдрайвер в качестве параметра
- (писала в выше - временная мера)
- еще - надо добавить немного гибкости
- ведь не всегда таймаут именно такого размера требуется (для gmail - порой и 25 секунд нужно)
- если
- реализовать assertThat с еще одним параметром - таймаутом
- то
- можно будет при вызове проверки - явно указывать размер таймаута
- это бывает удобно - когда в проекте есть проверки, требующие какого-то не стандартного таймаута
- в то же время
- для всех проверок указывать аймаут - уже перебор)
- потому - реализуем второй assertThat - уже без параметра - таймута
- в нем - вызовем первый assertThat и в качестве таймаута укажем значение Configuration.timeout
- Тут же, в core, реализуем класс Configuration со статическим полем timeout,
- в котором будет задан таймаут по умолчанию (разумно его установить равным 4 секунды)
- А если для тестов значения по умолчанию Configuration.timeout недостаточно
- то всегда есть возможность в тест-классе настроить самостоятельно Configuration.timeout
- Собственно - мы аналогично поступали - когда реализовывали версию этого задания на Selenide
- мы для этого теста изменяли значение Configuration.timeout
- */
- ****************************
- public class BaseTest {
- static WebDriver driver = new FirefoxDriver();
- @BeforeClass
- public static void setUp(){
- driver.manage().window().maximize();
- }
- @AfterClass
- public static void tearDown() {
- driver.quit();
- }
- }
- /*
- не виду смысла большого - максимизировать окно
- И еще неочевидный момент
- Если в предке НЕСКОЛЬКИХ тест-классов
- в static initialization block/при инициализации статических переменных создать вебдрайвер
- а в @AfterClass - удалять
- То уже при запуске ВСЕХ тестов - для тестов из второго тест-класса - начнутся проблемы
- т к @AfterClass-метод уже отработал (для предыдущего тест-класса)
- а вот static initialization block - уже не вызовется)
- он ведь вызывается вообще единожды - при начале работы с классом (а это уже случилось ранее)
- Был бы такой код в самом тест-классе - никаких проблем
- А в предке - лучше в BeforeClass создавай вебдрайвер
- */
- **************************************
- page.assertTrue(textToBePresentInElement(page.firstResult, subject));
- /*
- для начала - закомменть эти проверки
- добейся - чтоб без них - тест бегал
- затем - делай их
- что качается проверок
- ну, нет)
- не гибко, и не точно)
- не гибко - т к в случае если нужно проверить некий нн-ый ммейл в списке -
- то придется все добавлять и добавлять @FindBy элементов
- и это при том - что есть прокси-список для мейлов
- не точно - т к в конце нам нужна другая проверка - что мейл один + его текст - такой-то
- и ее в селенидовском решении - мы реализовали - универсальнее
- мы проверяли это - проверкой texts(...)
- элементов в списке = столько же, сколько переданных текстов
- текст каждого элемента списка - содержит соответственный текст
- (текст нулевого элемента - нулевой текст, первого - первый и т д)
- в селениуме - такого кондишена нету
- и мы его напишем и разместим в CustomConditions
- можно заглянуть в реализацию селенидовского кондишена - чтобы использовать
- идеи из логики проверки
- давай сначала разберемся с типом кондишена, который будем реализовывать
- можно реализовать ExpectedCondition<Boolean>
- тогда в случае успеха - wait.until - вернет True
- в случае не успеха - тест упадет (как ему и положено)
- а метод apply кондишена - будет возвращать
- True - если проверка пройдена
- False - если она не пройдена
- мы можем больше пользы извлечь из ситуации, если такой кондишен будет типа
- ExpectedCondition<List<WebElement>>
- тогда в случае успеха - wait.until вернет List<WebElement>
- и можно будет писать что-то такое - assertThat(sizeOf(elementsList,10)).get(9).click();
- (1 - если кондишен sizeOf был бы реализован с первым параметром - List<WebElement>)
- (2 - И если разобраться с дженерик-типами, а если нет - то просто задел на будущее)
- а метод apply кондишена ExpectedCondition<List<WebElement>> - будет возвращать
- List<WebElement> - тот же лейзи прокси список, который получил на входе - если проверка пройдена
- null - если она не пройдена
- так
- с типом кондишена определились
- нужен кондишен ExpectedCondition<List<WebElement>>
- теперь с параметрами
- проверяем - лейзи прокси список веб элементов - List<WebElement> elements
- что проверяем у этого списка - текстЫ = String... expectedTexts
- Загляни в селениумский класс ExpectedConditions
- (в коде - зажав ctrl + кликнув на имя любого стандартного селениумского кондишена)
- там можно почерпнуть ряд идей
- теперь - давай вспомним - что есть лейзи прокси список вебэлементов
- нам не надо заботиться о том, чтобы он переискался - он это делает самостоятельно
- а нам - в apply кондишена - надо принять решение - в ЭТОМ ФИКСИРОВАННОМ состоянии - список удовлетворяет условию или нет
- а ЭТО состояние - предполагает - что оно на протяжении анализа - не меняется)
- т е задача номер один = ЗАФИКСИРОВАТЬ = ЭТО состояние
- используй список строк для того, чтобы сохранить в нем тексты всех элементов нашего лейзи-прокси списка
- получили список актуальных текстов
- теперь - работаем ТОЛЬКО с ним - этто и есть ЭТО состояние )
- сравни размер списка актуальных текстов и количество переданных ожидаемых текстов
- если количество не равное - уже проверка не прошла
- если количество равное - сравнивай тексты
- нулевого актуального текста - с нулевым ожидаемым,
- первого - с первым и т д
- если какой-либо актуальный текст не содержит ожидаемого текста - проверка не прошла
- если тексты всех элементов прошли проверку - вот только в этом случае проверка прошла
- вспомни - зачем реализовывали метод toString у кондишена
- когда проверка не проходит - эта информация выводится в описании ошибки
- потому - тут мы должны описать - и актуальное (ЭТО состоняние - которое мы зафиксировали в списке строк)
- и ожидаемое состояние (переданные в кондишен ожидаемые тексты)
- тестируй написанный кондишен - используя
- проверку, которая должна пройти (она должна пройти)
- проверку, которая не должна пройти (она не должна пройти + сообщение об ошибке должно быть понятным)
- аналогично - для проверки текста нн-ого результата - напиши еще один кондишен
- nthElementHasText
- он попроще, подумай над его устройствром самостоятельно )
- оба кондишена
- и вообще - любые разрабатываемые самостоятельно кондишены - проверяй - как я выше описала
- особенно важно так делать на реальных проектах
- а иначе - даже не заводись с написанием кондишенов)
- т к ошибки, пропущенные на этом этапе - очень дорого обойдутся
- */
- **********************************************
- /*
- Кодишенов есть много и разных
- и далеко не все они ExpectedCondition<Boolean>
- вот в этих <...> - могут быть разные типы
- если совсем грубо - то результат вот этого ... типа и вернет нам WebDriverWait(...).until(...) -
- например
- есть такой кондишен ExpectedCondition<WebElement> visibilityOfElementLocated(By locator)
- https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html#visibilityOfElementLocated-org.openqa.selenium.By-
- это значит WebDriverWait(...).until(visibilityOfElementLocated(...)) - вернет вебэлемент, видимость которого мы ждали
- а для кондишенов ExpectedCondition<Boolean> - new WebDriverWait(...).until вернет результат типа Boolean
- это не отменяет главной схемы работы - если проверка new WebDriverWait(...).until( - не проходит за таймаут -
- то вызывается исключение
- но - помимо этого - есть вот такая разница ...
- пока наш assertThat возвращает void - нас это может не сильно тревожить )
- достаточно - в описании параметров убрать уточнение типа - не ExpectedCondition<Boolean>, а ExpectedCondition
- и assertThat - будет работать с любым из кондишенов
- правда - ничего нам не вернет)
- а вот если хочется получить результат new WebDriverWait(...).until( -
- да еще и не городить кучу методов - придется подумать и немного теории почитать
- */
- /*
- Давай разберемся. Сначала "идейно".
- Этот метод until возвращает такую интересную динамическую штуку-аватар...
- "воплощение" которого зависит от того что именно ты передашь вейт антилу параметром
- - точнее - какой именно кондишен ты ему передаешь...
- А кондишены бывают "разные"... "Разность" эта определеятся "параметром типа" который в джава записывается
- ИмяТипа<ЗДЕСЬ>
- Ты уже ведь использовал списки, например...
- В частности - List<WebElement>
- вот в такой записи типа - его параметром, выступает WebElement, указывая джаве,
- что это не просто список, а "список ВебЕлементов"
- а мог бы быть и списком стрингов например - List<String> или еще чего :)
- Такие "умные" типы которые получают параметр (или несколько параметров) - так и называются - параметризированными
- (а фича языка, в которой они поддерживаются называется - параметрическим полиформизмом -
- не путать с тем другим полиморфмизом... который называется - subtyping polymorphism,
- а в джава все его знают как просто "полиморфизм")...
- Еще они называются - шаблоны... или дженерики...
- Именно так - Java Generics - их принято называть в Java
- Так вот... таким же дженериком является тип ExpectedCondition
- который может быть параметризирован...
- Ты уже встречал запись типа:
- ExpectedCondition<Boolean>
- что же значит этот булеан? что он параметризирует?
- А он параметризирует внутреннюю реализацию этого кондишена...
- Он определяет то, каким должен быть тип возвращаемого значения в методе apply
- То есть...
- Если ты поставишь цель создать кондишен ExpectedCondition<Boolean> - ты должен будешь реализовать
- его метод apply - как возвращающий тип Boolean
- Но зачем вообще нам париться о типе возвращаемой сущности метода apply?
- А потому, что есть еще один "умный дженерик"...
- Только который не "параметризированный класс", а "параметризированный метод"
- (в джава бывают как Generic Types так и Generic Methods)
- и этот параметризированный метод - как раз и есть наш wait.until
- особенность реализации этого метода в том... что он вызывает внутри метод apply
- нашего кондишена... и запоминает то значение, которое этот метод apply возвращает...
- И потом... в конце всей истории... этот метод until - либо бросит исключение
- (в случае если "не дождется" кондишена)
- либо вернет то, что вернул метод apply в "случае успеха"...
- Получается... Если ты передашь вейт антилу кондишен параметризированный типом Boolean
- то в случае успеха антил - вернет тру...
- то есть ты можешь писать код вида:
- if (wait.until(enabled(composeButton))) {
- doSomething();
- }
- но на самом деле, такое нужно не часто...
- то есть - такие кондишены - которые
- параметризированы типом булеан...
- бошьше толку как раз от кондишенов параметризированных типом "того, характеристики чего ждет кондишен"...
- вот кондишен visibilityOf - как раз параметризирован типом WebElement
- ExpectedCondition<WebElement>
- и его метод apply возвращает обьект типа WebElement,
- а "идейно" - возвращает этот же элемент, визибилити которого мы дожидались...
- (если бы мы не дождались - apply вернул бы null - что в этом случае играет роль "false")
- а until в конце концов вернет то, что вернет apply
- и именно поэтому мы можем писать такой код:
- wait.until(visibilityOf(composeButton)).click()
- теперь твоим заданием будет - переделать assertThat что бы он возвращал то что возвращает wait.until
- ну и потом - переделать все свои кондишены, что бы они были параметризированы типом той сущности,
- характеристики которой они должни дожидаться (кстати так выражатся немного некорректно - ведь кондишен не ждет,
- он только выдает информацию - да/нет - а ждет уже вейт антил, ну то такое:) )
- для того, чтобы "правильно реализовать этот wait.until" -
- тебе придется разобраться с этими дженериками...
- И с дженерик типами и дженерик методами.
- И даже этого будет мало...
- Прийдется покопаться в коде селениума, чтобы понять, что там за тип реально получает
- вейт антил как кондишен...
- Потому что он получает не ExpectedCondition а что то еще более странное...
- Это работает - потому что это "странное" - есть родительским классом для ExpectedCondition
- */
- /*
- Что почитать про Generics
- про дженерики в общем(русский)
- http://www.quizful.net/post/java-generics-tutorial
- http://www.tutorialspoint.com/java/java_generics.htm
- http://developer.alexanderklimov.ru/android/java/generic.php
- конвеншенcы http://stackoverflow.com/questions/2900881/generic-type-parameter-naming-convention-for-java-with-multiple-chars
- https://docs.oracle.com/javase/tutorial/java/generics/types.html
- уроки
- http://docs.oracle.com/javase/tutorial/extra/generics/index.html
- очень приличный faq (есть pdf, и есть кое-что еще, помимо дженериков)
- http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
- */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement