Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- public class BaseTest {
- {
- System.setProperty("webdriver.chrome.driver", "D:\\chromedriver.exe");
- }
- @AfterClass
- public static void tearDown(){
- driver.quit();
- }
- }
- /*
- Читаем код
- судя по всему - переменная driver - инициализируется в месте, где она объявлена - pages.GMail
- путь к хром драйверу - мы задаем - в предке тест-класса, в instance initialisation block
- а это значит - это будет выполняться перед запуском каждого тест-метода
- и в предке же тест-класса в AfterClass - мы закрываем драйвер
- получаем не самую простую для понимания картинку
- а самое главное - не надежную
- практически всегда оправдан подход - где ті создаешь сущность, там ее и убивай
- если тебе нужно убивать вебдрайвер в предке тест-класса, значит - и инициализируй ее тут же
- в этом же классе
- такой подход позволит сделать код проще для понимания
- и убережет тебя от различных ошибок, которые ловить не очень-то легко
- пример
- код - как у тебя сейчас
- но 2 тест-класса (с одним предком BaseTest)
- получаем результат
- если запустим на выполнение тесты одного тест-класса / один тест = все ок
- а если запустим на выполнение тесты двух тест-классов - то почему-то для второго тест-класса - не будет у нас инициалирированного драйвера
- и поди пойми)
- поясняю
- драйвер инициализируется - при первом обращении к pages.GMail (судя по твоей реализации)
- выполнятся тесты одного тест-класса - выполнится AfterClass-метод
- запустятся на выполнение тесты из другого тест-класса
- напоминаю - к pages.GMail мы уже обращались - значит - сейчас уже ничего инициализироваться не будет
- и как следствие - нету драйвера для тестов из второго тест-класса
- как стоит реализовать
- объявить переменную driver - в предке тест-класса
- инициализировать - в BeforeClass-методе
- там же, перед инициализацией, выполнить System.setProperty("webdriver.chrome.driver", "D:\\chromedriver.exe");
- закрыть - в @AfterClass методе
- код - простой, понятный и надежный )
- да, возникнут вопросы - откуда брать драйвер в пейдже
- то решим)
- это точно не повод усложнять)
- Какие вообще наши варианты
- т е для варианта - свой вебдрайвер для каждого тест-метода
- открывать можно
- при инициализации не статической переменной
- instance initialization block
- Before метод
- закрывать
- After метод
- а для варианта - один вебдрайвер на все тест-методы одного класса
- (такой вариант подходит, если все тесты запускать в одном потоке - как мы до этого и делали)
- открывать можно
- при инициализации статической переменной
- static initialization block
- /*
- эти 2 варианта надо применять с осторожностью на уровне предков тест-класса - тоже могут быть проблемы
- подобные тем, что я выше описывала
- все же BeforeClass метод - лучшее решение
- и из этих соображений
- */
- BeforeClass метод
- закрывать
- AfterClass метод
- какой вариант выбрать - зависит от обстоятельств
- просто - важно понимать разницу и выбирать наиболее подходящий в данном случае
- Полезные линки
- http://www.javamadesoeasy.com/2015/06/differences-between-instance.html
- http://stackoverflow.com/questions/15493189/beforeclass-vs-static
- Получим более содержательное сообщение об ошибке - если работаем с аннотированными методами
- http://www.unknownerror.org/opensource/junit-team/junit/q/stackoverflow/512184/best-practice-initialize-junit-class-fields-in-setup-or-at-declaration
- http://www.javaworld.com/article/2076265/testing-debugging/junit-best-practices.html
- (подзаголовок Do not use the test-case constructor to set up a test case)
- */
- ***************************************
- /*
- Итак - есть у нас задача
- теперь в пейджи нужно передать драйвер
- из пейджей доступаться до даных класса-предка тест-класса - это неверно
- т к пейдж - инструмент
- он - вспомогательный
- а когда инструмент просит что-то вспомогательное от сущностей, для которых он и создан -
- это слишком запутано получается)
- самый простой спомоб - перейти от использования пейджей-модулей
- к использованию пейджей-объектов
- и в конструктор пейджа - передавать драйвер
- и его внутри пейджа запоминать в спец поле
- и в методах пейджа - использовать вот этот вебдрайвер
- эта логика - будет характерна всем 3-м пейджам
- значит - ее можно вынести на уровень предка пейджа
- реализуй BasePage
- с конструктором и соответствующим полем - для вебдрайвера
- отнаследуй пейджи от этого класса
- учти - что это пейжи-модули
- вспомни про конвеншенсы для имен классов
- уже в Gmail - не нужно обїявлять и инициализиовать вебдрайвер
- как и в других пейджах - будем инициализировать пейдж, передавая в его конструктор вебдрайвер из тест-класса
- и для всех методов пейджей - вебдрайвер из тест-класса станет доступным
- в самом тест-классе - инициализацию пейджей тоже поменяй - с учетом этих изменений
- */
- ***********************************************
- driver.get("https://gmail.com");
- /*
- есть куча полезных вещей, которая нам бы пригодилась
- вот например open(String url) - универсальная и полезная функциональность
- будем ее віносить в отдельный класс
- пусть называется например ConciseAPI
- только и тут проблема - там тоже нету драйвера
- поступим пока не учень красиво зато сравнительно просто для понимания
- будем в каждый метод класса ConciseAPI там где требуется вебдрайвер - передавать вебдрайвер как параметр
- потом это улучшим
- а пока пусть так будет
- */
- ***************************
- driver.findElement(By.id("Email"))....
- /*
- есть у нас код - мы находим элемент и как-то с ним работаем
- но - мы при таком коде ровно в момент вызова кода - получаем элемент и с ним работаем
- если на этот момент - элемент не закгрузился (просто не успел)
- нам бы не помешал метод
- который принимал бы локатор By
- и возвращал вебэлемент
- и реализован был так -
- сначала - ожидание видимости элемента
- затем - его получение по локатору и возврат
- разумно назвать такой метод $
- и реализовать в ConciseAPI
- и в пейджах - там где мы работаем с элементами - уже оперировать этим методом
- */
- *********************
- public static void send(String mail, String subject){
- /*
- в Selenide - ты использовала метод byText
- фактически - если посмотреть как он реализован - внутри он использует By.xPath
- посмотри на реализацию в Selenide(на само xPath-выражение)
- или сама выражение попроще примени
- реализуй в ConciseAPI - метод byText и тут используй
- */
- **********************************
- public static void assertMails(String... emailTexts) {
- for (int i = 0; i < emailTexts.length; i++) {
- (new WebDriverWait(driver, 6)).until(
- textToBePresentInElementLocated(emails, emailTexts[i]));
- }
- }
- /*
- для віполнения ждущей проверки - реализуй метод assertThat в ConciseAPI
- код немного упростится -
- вместо new WebDriverWait(driver, 6)).until(....)
- будет assertThat(driver, ....)
- и нужен новый кондишен )
- у нас есть список мейлов.
- и есть текстЫ, которые должны содержать мейлы из списка
- вспомни эту задачу на селениде - как мы это проверяли
- второй момент - кондишен и его параметр
- нам нужен аналог селенидовского texts
- причем такой кондишен должен работать для списка элементов
- (как и в предыдущем случае - реши самостоятельно - какого тапа будет параметр для списка элементов
- - List<WebElement> или By)
- давай реализуем такой кондишен в CustomConditions
- */
- ***************************
- public static void assertMail(int index, String emailText) {
- String cssSelectorForMail = "[role='main'] .zA:nth-child("+index+")";
- (new WebDriverWait(driver, 6)).until(
- textToBePresentInElementLocated(By.cssSelector((cssSelectorForMail)), emailText));
- }
- /*
- и тут мы поступим также - воспользуемся assertThat(driver, ....)
- и реализуем новый кондишен
- listNthElementHasText(final ... elements, final int index, final String expectedText)
- который для проверит текст нн-ого элемента
- уже мы писали кондишены, правда попроще
- теперь - усложним задачку
- но сначала давай обговорим тип первого параметра
- мы можем написать кондишен для List<WebElement> elements
- или для By elements
- если выбираешь первый вариант - пишем кондишен для лейзи прокси списка (полученного через FindBy)
- написать и тот, и тот кондишен - примерно одинаково усилий нужно
- а вот чтобы вызвать кондишен с List<WebElement> elements - тебе побольше придется постараться)
- решай сама
- как написать такой кондишен - ниже подскажу
- */
- /*
- Мы определились, что нам нужны 2 новых кондишена,
- у которых первый параметр - это List<WebElement> или By = список элементов,
- первый кондишен - textsOf(... elements, String... texts)
- где параметры - список элементов (или его локатор) и текстЫ которые мы будем проверять для каждого из элементов
- второй кондишен - listNthElementHasText(.... elements, int index, String text)
- где параметры - список элементов(или его локатор), индекс проверяемого элемента
- и текст который мы для него проверим
- чтоб не придумывать велосипед, давай посмотрим на код 2-ух стандартных селениумских кондишенов,
- один из них работает со списком элементов и этим нам интересен
- второй - с текстом и этим нам интересен
- стандартные кондишены реализованы в классе ExpectedConditions
- напиши ExpectedConditions в своем коде
- зажми ctrl+кликни на ExpectedConditions
- откроется модуль и там можно будет посмотреть код кондишенов
- Попробуй по аналогии с приведенным примером организовать свои кондишены
- У тебя есть лекция Якова, которая рассказывает, как кондишены устроены
- У тебя есть пример кондишена с первым параметром такого же типа, как нам нужно
- У тебя есть пример кондишена, который проверяет текст
- Ты можешь открыть код селенидовского кондишена texts or exactTexts и тоже взять оттуда идеи
- Мы определились с тем, что наши новые кондишены должны делать, с их именами и парамерами
- Попробуй написать эти 2 кондишена.
- Будет сложно - обращайся, будем упрощать задачу)
- Использовать List<WebElement> или By = список элементов = зависит от того,
- будешь ли ты пейджи реализовывать с использованием @FindBy элементов
- пока ты использовала локаторы By
- мне тоже такой вариант кажется разумнее
- можно на этом варианте и остановиться
- */
- *************************************
- By.cssSelector("[title='Sent Mail']")
- By.cssSelector("[title~='Inbox']")
- /*
- и для таких вещей - можно реализовать методы byTitle / byExactTitle
- */
- ***********************************
- https://docs.google.com/document/d/1BiYTLdypDfucSqiY9isv1HCKKQIxelzqYrN-3Ku1RWM/edit?usp=sharing
- /*
- вот это еще почитай
- если сможешь разобраться - ок
- а нет - давай обсудим
- я ниже приведу текст по дженерик-типам - тоже надо бы разобраться
- */
- /*
- Кодишенов есть много и разных
- и далеко не все они 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
- ----------------------------------------------------------
- для понимания кетчера (Catcher)
- http://grepcode.com/file/repo1.maven.org/maven2/com.google.guava/guava/r06/com/google/common/base/Function.java
- http://www.programcreek.com/java-api-examples/com.google.common.base.Function
- https://docs.oracle.com/javase/tutorial/java/generics/lowerBounded.html
- http://stackoverflow.com/questions/3847162/java-generics-super-keyword
- http://stackoverflow.com/questions/2800369/bounding-generics-with-super-keyword
- http://stackoverflow.com/questions/1910892/what-is-the-difference-between-super-and-extends-in-java-generics
- если осилишь после этого и применение кетчера(то, что в прошлом ревью отложили) - можно уже сейчас
- по-прежнему - кетчера можно отложить
- */
Advertisement
Add Comment
Please, Sign In to add comment