Advertisement
julia_v_iluhina

Untitled

Dec 28th, 2016
115
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 27.46 KB | None | 0 0
  1. public class GmailPage {
  2.  
  3. /*
  4.     с инициализацией пейджа - все ок
  5. */
  6.     //  refresh the page
  7.     @FindBy(xpath = "//div[contains(@class, 'asf')]")
  8.     WebElement refresh;
  9. /*
  10.     тут через css - можно чуть проще переписать
  11.     ну то такое - мелочь
  12.     погоду в данном случае не сделает
  13. */
  14.  
  15.     //  locator of first found letter
  16.     @FindBy(css = ".srg .g:nth-child(1)")
  17.     public WebElement firstResult;
  18. /*
  19.     а вот это - с другой оперы ) - из google search
  20.     в любом случае
  21.     у тебя выше есть emails
  22.     и нам его хватит - если говорить о работе со списком мейлов
  23. */
  24.  
  25.   public void setLogin(String email, String password) {
  26.         loginEmail.sendKeys(email, Keys.ENTER);
  27.         loginPassword.sendKeys(password, Keys.ENTER);
  28.   }
  29. /*
  30.     в чем особенность селениумских прокси элементов/списков (те, что @FindBy аннотированы)
  31.     да, они переискиваются при обращении к ним
  32.     НО - не ждут)
  33.     переискали, и даже если что-то там еще недогрузилось - вернули нам то, что есть на этот момент)
  34.     так что - неудивительно, что тормознутый GMail давал тебе такого рода ошибки)
  35.  
  36.     когда мы можем кликнуть, даблкликнуть и т д
  37.     когда элемент - уже видим
  38.     значит - нам нужно этого дождаться
  39.  
  40.     поскольку - это частое сочетание - дождаться видимости элемента + что-то там с ним сделать
  41.     реализуй метод WebElement $(WebDriver driver, WebElement proxyElement)
  42.     в котором - сначала - дожидайся видимости элемента
  43.     затем - возвращай его же
  44.     поскольку метод - универсальный
  45.     расположи его в классе-контейнере универсальных методов (пусть будет ConciseAPI, например)
  46.     пока - в этой версии - вебдрайвер - передадим в метод как параметр
  47.  
  48.     и тогда - тут и далее - используй метод  $
  49.     $(driver, loginEmail).sendKeys(email, Keys.ENTER);
  50.     и все ок будет)
  51.  
  52.     вот тебе еще один пример - когда в методе-действии
  53.     мы используем ждущую проверку
  54.     по сути - такая проверка - выполняет не функции собственно тестовой проверки
  55.     а функции умного ожидания - нам нужно дождаться верной тестовой ситуации
  56.     перед тем как что-то выполнить с элементом - нам нужна его видимость
  57.  
  58.     этот момент учти для всех прокси веб-элементов
  59.  
  60.     полезные линки
  61.     https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html
  62.     http://selenium2advanced.blogspot.com/2013/08/working-with-page-factory-and-webdriver.html
  63.     https://github.com/SeleniumHQ/selenium/wiki/PageFactory
  64.  
  65.     на самом деле - это последняя работа - где мы используем @FindBy элементы и списки
  66.     советую разобраться с этим тут - на случай  - вдруг пригодится
  67.     а вообще - не самая замечательно реализованная вещь)
  68.     но для разбора  - как @FindBy работает и с чем его едят - полезно разочек поработать
  69.         для пейджа Selenium-решения -
  70.                 вариант без @FindBy - тоже предпочтительнее
  71.                 вот почему:
  72.                 - гораздо менее лаконичный и менее читабельный
  73.                 - магичный и не ооп-шный  - не консистентный со всем другим кодом создания прокси элементов, и вообще классов в проекте -
  74.                   а значит ньюкамерам сложнее въехать
  75.                 - Page Factory в принципе зло.
  76.                   Этого Яков касается в докладе https://www.youtube.com/watch?v=Hi6EYEkSvrk
  77.                   Page Factory вся наша тусовка кстати - считает злом.
  78.                         И Пирогов
  79.                         И Солнцев
  80.                         и главное - Баранцев (коммиттер в селениум) .
  81.                   Есть планы ее выкинуть с Селениума вообще
  82.  
  83. */
  84. **************************
  85.     public void assertTrue(ExpectedCondition<Boolean> condition) {
  86.         (new WebDriverWait(driver, 10)).until(condition);
  87.     }
  88. /*
  89.     Это метод - точно не метод пейджа
  90.     вынеси его в ConciseAPI тоже
  91.  
  92.     да и тебе не всегда будет нужен вариант работы с кондишеном ExpectedCondition<Boolean>
  93.  
  94.     пока метод возвращает void - можно вообще не ограничивать себя - и работать с ExpectedCondition (не уточняя дженерик-типа)
  95.     с дженерик-типом - конечно, тоже придется разобраться
  96.     но это можно сделать и позже)
  97.  
  98.     по финишу - почитаешь
  99.     если вопросов не будет - применишь
  100.     если будет - ограничься пока версией метода без уточнения дженерик-типа для ExpectedCondition
  101.  
  102.     раз так - то корректнее будет назвать метод assertThat
  103.     и еще - поскольку вынесем метод в ConciseAPI - нужно в метод передавать и вебдрайвер в качестве параметра
  104.     (писала в выше - временная мера)
  105.  
  106.     еще - надо добавить немного гибкости
  107.  
  108.     ведь не всегда таймаут именно такого размера требуется (для gmail - порой и 25 секунд нужно)
  109.  
  110.     если
  111.     реализовать assertThat с еще одним параметром - таймаутом
  112.     то
  113.     можно будет при вызове проверки  - явно указывать размер таймаута
  114.     это бывает удобно - когда в проекте есть проверки, требующие какого-то не стандартного таймаута
  115.  
  116.     в то же время
  117.     для всех проверок указывать аймаут - уже перебор)
  118.     потому - реализуем второй assertThat - уже без параметра - таймута
  119.     в нем - вызовем первый assertThat и в качестве таймаута укажем значение Configuration.timeout
  120.  
  121.     Тут же, в core, реализуем класс Configuration со статическим полем timeout,
  122.     в котором будет задан таймаут по умолчанию (разумно его установить равным 4 секунды)
  123.  
  124.     А если для тестов значения по умолчанию Configuration.timeout недостаточно
  125.     то всегда есть возможность в  тест-классе настроить самостоятельно Configuration.timeout
  126.  
  127.     Собственно - мы аналогично поступали - когда реализовывали версию этого задания на Selenide
  128.     мы для этого теста изменяли значение Configuration.timeout
  129. */
  130. ****************************
  131. public class BaseTest {
  132.  
  133.     static WebDriver driver = new FirefoxDriver();
  134.  
  135.     @BeforeClass
  136.     public static void setUp(){
  137.         driver.manage().window().maximize();
  138.     }
  139.  
  140.     @AfterClass
  141.     public static void tearDown() {
  142.         driver.quit();
  143.     }
  144. }
  145. /*
  146.     не виду смысла большого - максимизировать окно
  147.  
  148.     И еще неочевидный момент
  149.             Если в предке НЕСКОЛЬКИХ тест-классов
  150.                 в static initialization block/при инициализации статических переменных создать вебдрайвер
  151.  
  152.                 а в @AfterClass - удалять
  153.  
  154.             То уже при запуске ВСЕХ тестов - для тестов из второго тест-класса - начнутся проблемы
  155.                 т к @AfterClass-метод уже отработал (для предыдущего тест-класса)
  156.                 а вот static initialization block - уже не вызовется)
  157.                 он ведь вызывается вообще единожды - при начале работы с классом (а это уже случилось ранее)
  158.  
  159.             Был бы такой код в самом тест-классе - никаких проблем
  160.             А в предке - лучше в  BeforeClass создавай вебдрайвер
  161. */
  162. **************************************
  163. page.assertTrue(textToBePresentInElement(page.firstResult, subject));
  164. /*
  165.     для начала - закомменть эти проверки
  166.     добейся - чтоб без них - тест бегал
  167.     затем - делай их
  168.  
  169.     что качается проверок
  170.     ну, нет)
  171.     не гибко, и не точно)
  172.  
  173.     не гибко - т к в случае если нужно проверить некий нн-ый ммейл в списке -
  174.     то придется все добавлять и добавлять @FindBy элементов
  175.     и это при том - что есть прокси-список для мейлов
  176.  
  177.     не точно - т к в конце нам нужна другая проверка - что мейл один + его текст - такой-то
  178.     и ее в селенидовском решении - мы реализовали - универсальнее
  179.     мы проверяли это - проверкой texts(...)
  180.         элементов в списке = столько же, сколько переданных текстов
  181.         текст каждого элемента списка - содержит соответственный текст
  182.         (текст нулевого элемента - нулевой текст, первого - первый и т д)
  183.           в селениуме - такого кондишена нету
  184.             и мы его напишем и разместим в CustomConditions
  185.  
  186.             можно заглянуть в реализацию селенидовского кондишена - чтобы использовать
  187.             идеи из логики проверки
  188.  
  189.             давай сначала разберемся с типом кондишена, который будем реализовывать
  190.             можно реализовать ExpectedCondition<Boolean>
  191.             тогда в случае успеха - wait.until - вернет True
  192.             в случае не успеха - тест упадет (как ему и положено)
  193.             а метод apply кондишена - будет возвращать
  194.             True - если проверка пройдена
  195.             False - если она не пройдена
  196.  
  197.             мы можем больше пользы извлечь из ситуации, если такой кондишен будет типа
  198.             ExpectedCondition<List<WebElement>>
  199.             тогда в случае успеха - wait.until вернет List<WebElement>
  200.             и можно будет писать что-то такое - assertThat(sizeOf(elementsList,10)).get(9).click();
  201.             (1 - если кондишен sizeOf был бы реализован с первым параметром - List<WebElement>)
  202.             (2 - И если разобраться с дженерик-типами, а если нет - то просто задел на будущее)
  203.  
  204.             а метод apply кондишена ExpectedCondition<List<WebElement>> - будет возвращать
  205.                 List<WebElement> - тот же лейзи прокси список, который получил на входе - если проверка пройдена
  206.                 null - если она не пройдена
  207.  
  208.             так
  209.             с типом кондишена определились
  210.             нужен кондишен ExpectedCondition<List<WebElement>>
  211.  
  212.             теперь с параметрами
  213.             проверяем  - лейзи прокси список веб элементов - List<WebElement> elements
  214.             что проверяем у этого списка - текстЫ = String... expectedTexts
  215.  
  216.             Загляни в  селениумский класс ExpectedConditions
  217.             (в коде - зажав ctrl + кликнув на имя любого стандартного селениумского кондишена)
  218.             там можно почерпнуть ряд идей
  219.  
  220.             теперь - давай вспомним - что есть лейзи прокси список вебэлементов
  221.             нам не надо заботиться о том, чтобы он переискался - он это делает самостоятельно
  222.             а нам - в apply кондишена - надо принять решение - в ЭТОМ ФИКСИРОВАННОМ состоянии - список удовлетворяет условию или нет
  223.             а ЭТО состояние - предполагает - что  оно на протяжении анализа - не меняется)
  224.  
  225.             т е задача номер один = ЗАФИКСИРОВАТЬ = ЭТО состояние
  226.             используй список строк для того, чтобы сохранить в нем тексты всех элементов нашего лейзи-прокси списка
  227.  
  228.             получили список актуальных текстов
  229.  
  230.             теперь - работаем ТОЛЬКО с ним - этто и есть ЭТО состояние )
  231.             сравни размер списка актуальных текстов и количество переданных ожидаемых текстов
  232.             если количество не равное - уже проверка не прошла
  233.  
  234.             если количество равное - сравнивай тексты
  235.                 нулевого актуального текста - с нулевым ожидаемым,
  236.                 первого - с первым и т д
  237.                 если какой-либо актуальный текст не содержит ожидаемого текста - проверка не прошла
  238.                 если тексты всех элементов прошли проверку - вот только в этом случае проверка прошла
  239.  
  240.              вспомни - зачем реализовывали метод toString у кондишена
  241.                  когда проверка не проходит - эта информация выводится в описании ошибки
  242.                  потому - тут мы должны описать - и актуальное (ЭТО состоняние - которое мы зафиксировали в списке строк)
  243.                  и ожидаемое состояние (переданные в кондишен ожидаемые тексты)
  244.  
  245.             тестируй написанный кондишен - используя
  246.                  проверку, которая должна пройти (она должна пройти)
  247.                  проверку, которая не должна пройти (она не должна пройти + сообщение об ошибке должно быть понятным)
  248.  
  249.         аналогично - для проверки текста нн-ого результата - напиши еще один кондишен
  250.         nthElementHasText
  251.         он попроще, подумай над его устройствром самостоятельно )
  252.  
  253.         оба кондишена
  254.         и вообще - любые разрабатываемые самостоятельно кондишены - проверяй - как я выше описала
  255.         особенно важно так делать на реальных проектах
  256.         а иначе - даже не заводись с написанием кондишенов)
  257.         т к ошибки, пропущенные на этом этапе - очень дорого обойдутся
  258. */
  259. **********************************************
  260. /*
  261.  Кодишенов есть много и разных
  262.     и далеко не все они ExpectedCondition<Boolean>
  263.     вот в этих <...> - могут быть разные типы
  264.     если совсем грубо - то результат вот этого ... типа и вернет нам WebDriverWait(...).until(...) -
  265.  
  266.     например
  267.          есть такой кондишен ExpectedCondition<WebElement> visibilityOfElementLocated(By locator)
  268.          https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html#visibilityOfElementLocated-org.openqa.selenium.By-
  269.  
  270.          это значит WebDriverWait(...).until(visibilityOfElementLocated(...)) - вернет вебэлемент, видимость которого мы ждали
  271.  
  272.          а для кондишенов ExpectedCondition<Boolean> - new WebDriverWait(...).until вернет результат типа Boolean
  273.  
  274.          это не отменяет главной схемы работы - если проверка new WebDriverWait(...).until( - не проходит за таймаут -
  275.          то вызывается исключение
  276.  
  277.          но - помимо этого - есть вот такая разница ...
  278.  
  279.     пока наш assertThat возвращает void  - нас это может не сильно тревожить )
  280.     достаточно - в описании параметров убрать уточнение типа - не ExpectedCondition<Boolean>, а ExpectedCondition
  281.     и assertThat - будет работать с любым из кондишенов
  282.     правда - ничего нам не вернет)
  283.  
  284.     а вот если хочется получить результат new WebDriverWait(...).until( -
  285.     да еще и не городить кучу методов - придется подумать и немного теории почитать
  286.  
  287. */
  288.  
  289. /*
  290. Давай разберемся. Сначала "идейно".
  291.  
  292. Этот метод until возвращает такую интересную динамическую штуку-аватар...
  293. "воплощение" которого зависит от того что именно ты передашь вейт антилу параметром
  294. - точнее - какой именно кондишен ты ему передаешь...
  295.  
  296. А кондишены бывают "разные"... "Разность" эта определеятся "параметром типа" который в джава записывается
  297. ИмяТипа<ЗДЕСЬ>
  298.  
  299. Ты уже ведь использовал списки, например...
  300. В частности - List<WebElement>
  301.  
  302. вот в такой записи типа - его параметром, выступает WebElement, указывая джаве,
  303. что это не просто список, а "список ВебЕлементов"
  304. а мог бы быть и списком стрингов например - List<String> или еще чего :)
  305.  
  306. Такие "умные" типы которые получают параметр (или несколько параметров) - так и называются - параметризированными
  307. (а фича языка, в которой они поддерживаются называется - параметрическим полиформизмом -
  308. не путать с тем другим полиморфмизом... который называется - subtyping polymorphism,
  309. а в джава все его знают как просто "полиморфизм")...
  310. Еще они называются - шаблоны... или дженерики...
  311. Именно так - Java Generics - их принято называть в Java
  312.  
  313. Так вот... таким же дженериком является тип ExpectedCondition
  314. который может быть параметризирован...
  315.  
  316. Ты уже встречал запись типа:
  317. ExpectedCondition<Boolean>
  318.  
  319. что же значит этот булеан? что он параметризирует?
  320. А он параметризирует внутреннюю реализацию этого кондишена...
  321. Он определяет то, каким должен быть тип возвращаемого значения в методе apply
  322.  
  323. То есть...
  324. Если ты поставишь цель создать кондишен ExpectedCondition<Boolean> - ты должен будешь реализовать
  325. его метод apply -  как возвращающий тип Boolean
  326.  
  327. Но зачем вообще нам париться о типе возвращаемой сущности метода apply?
  328.  
  329. А потому, что есть еще один "умный дженерик"...
  330. Только который не "параметризированный класс", а "параметризированный метод"
  331. (в джава бывают как Generic Types так и Generic Methods)
  332.  
  333. и этот параметризированный метод - как раз и есть наш wait.until
  334.  
  335. особенность реализации этого метода в том... что он вызывает внутри метод apply
  336. нашего кондишена... и запоминает то значение, которое этот метод apply возвращает...
  337.  
  338. И потом... в конце всей истории... этот метод until - либо бросит исключение
  339. (в случае если "не дождется" кондишена)
  340. либо вернет то, что вернул метод apply в "случае успеха"...
  341.  
  342. Получается... Если ты передашь вейт антилу кондишен параметризированный типом Boolean
  343. то в случае успеха антил - вернет тру...
  344.  
  345. то есть ты можешь писать код вида:
  346.  
  347. if (wait.until(enabled(composeButton))) {
  348.    doSomething();
  349. }
  350.  
  351. но на самом деле, такое нужно не часто...
  352. то есть - такие кондишены - которые
  353. параметризированы типом булеан...
  354.  
  355. бошьше толку как раз от кондишенов параметризированных типом "того, характеристики чего ждет кондишен"...
  356.  
  357. вот кондишен visibilityOf - как раз параметризирован типом WebElement
  358.     ExpectedCondition<WebElement>
  359.  
  360. и его метод apply возвращает обьект типа WebElement,
  361. а "идейно" - возвращает этот же элемент, визибилити которого мы дожидались...
  362. (если бы мы не дождались - apply вернул бы null - что в этом случае играет роль "false")
  363.  
  364. а until в конце концов вернет то, что вернет apply
  365. и именно поэтому мы можем писать такой код:
  366.  
  367.     wait.until(visibilityOf(composeButton)).click()
  368.  
  369. теперь твоим заданием будет - переделать assertThat что бы он возвращал то что возвращает wait.until
  370.  
  371. ну и потом - переделать все свои кондишены, что бы они были параметризированы типом той сущности,
  372. характеристики которой они должни дожидаться (кстати так выражатся немного некорректно - ведь кондишен не ждет,
  373. он только выдает информацию - да/нет - а ждет уже вейт антил, ну то такое:) )
  374.  
  375. для того, чтобы "правильно реализовать этот wait.until" -
  376. тебе придется разобраться с этими дженериками...
  377. И с дженерик типами и дженерик методами.
  378.  
  379. И даже этого будет мало...
  380. Прийдется покопаться в коде селениума, чтобы понять, что там за тип реально получает
  381. вейт антил как кондишен...
  382. Потому что он получает не ExpectedCondition а что то еще более странное...
  383. Это работает - потому что это "странное" - есть родительским классом для ExpectedCondition
  384.  
  385. */
  386.  
  387. /*
  388.     Что почитать про Generics
  389.     про дженерики в общем(русский)
  390.     http://www.quizful.net/post/java-generics-tutorial
  391.  
  392.     http://www.tutorialspoint.com/java/java_generics.htm
  393.     http://developer.alexanderklimov.ru/android/java/generic.php
  394.  
  395.     конвеншенcы      http://stackoverflow.com/questions/2900881/generic-type-parameter-naming-convention-for-java-with-multiple-chars
  396.     https://docs.oracle.com/javase/tutorial/java/generics/types.html
  397.  
  398.     уроки
  399.     http://docs.oracle.com/javase/tutorial/extra/generics/index.html
  400.  
  401.     очень приличный faq (есть pdf, и есть кое-что еще, помимо дженериков)
  402.     http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
  403. */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement