julia_v_iluhina

Untitled

Dec 5th, 2016
83
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 29.11 KB | None | 0 0
  1. public class BaseTest {
  2.     {
  3.         System.setProperty("webdriver.chrome.driver", "D:\\chromedriver.exe");
  4.     }
  5.     @AfterClass
  6.     public static void tearDown(){
  7.         driver.quit();
  8.     }
  9. }
  10.  
  11. /*
  12.           Читаем код
  13.           судя по всему - переменная driver - инициализируется в месте, где она объявлена - pages.GMail
  14.  
  15.           путь к хром драйверу - мы задаем - в предке тест-класса, в instance initialisation block
  16.           а это значит - это будет выполняться перед запуском каждого тест-метода
  17.  
  18.           и в  предке же тест-класса в AfterClass - мы закрываем драйвер
  19.  
  20.           получаем не самую простую для понимания картинку
  21.           а самое главное - не надежную
  22.  
  23.           практически всегда оправдан подход - где ті создаешь сущность, там ее и убивай
  24.           если тебе нужно убивать вебдрайвер в предке тест-класса, значит - и инициализируй ее тут же
  25.           в этом же классе
  26.  
  27.           такой подход позволит сделать код проще для понимания
  28.           и убережет тебя от различных ошибок, которые ловить не очень-то легко
  29.  
  30.           пример
  31.           код - как у тебя сейчас
  32.           но 2 тест-класса (с одним предком BaseTest)
  33.  
  34.           получаем результат
  35.             если запустим на выполнение тесты одного тест-класса / один тест = все ок
  36.             а если запустим на выполнение тесты двух тест-классов - то почему-то для второго тест-класса - не будет у нас инициалирированного драйвера
  37.  
  38.           и поди пойми)
  39.             поясняю
  40.             драйвер инициализируется - при первом обращении к pages.GMail (судя по твоей реализации)
  41.             выполнятся тесты одного тест-класса - выполнится AfterClass-метод
  42.             запустятся на выполнение тесты из другого тест-класса
  43.             напоминаю - к pages.GMail мы уже обращались - значит  - сейчас уже ничего инициализироваться не будет
  44.             и как следствие - нету драйвера для тестов из второго тест-класса
  45.  
  46.           как стоит реализовать
  47.             объявить переменную driver - в предке тест-класса
  48.             инициализировать - в BeforeClass-методе
  49.             там же, перед инициализацией, выполнить System.setProperty("webdriver.chrome.driver", "D:\\chromedriver.exe");
  50.             закрыть - в @AfterClass методе
  51.           код - простой, понятный и надежный )
  52.           да, возникнут вопросы - откуда брать драйвер в пейдже
  53.           то решим)
  54.           это точно не повод усложнять)
  55.  
  56.           Какие вообще наши варианты
  57.                     т е для варианта - свой вебдрайвер для каждого тест-метода
  58.                         открывать можно
  59.                             при инициализации не статической переменной
  60.                             instance initialization block
  61.                             Before метод
  62.                         закрывать
  63.                             After метод
  64.  
  65.                     а для варианта - один вебдрайвер на все тест-методы одного класса
  66.                     (такой вариант подходит, если все тесты запускать в одном потоке - как мы до этого и делали)
  67.                         открывать можно
  68.                            при инициализации статической переменной
  69.                            static initialization block
  70.                            /*
  71.                                 эти 2 варианта надо применять с осторожностью на уровне предков тест-класса - тоже могут быть проблемы
  72.                                 подобные тем, что я выше описывала
  73.  
  74.                                 все же BeforeClass метод - лучшее решение
  75.                                 и из этих соображений
  76.                            */
  77.                            BeforeClass метод
  78.                         закрывать
  79.                            AfterClass метод
  80.  
  81.                     какой вариант выбрать - зависит от обстоятельств
  82.                     просто - важно понимать разницу и выбирать наиболее подходящий в данном случае
  83.  
  84.             Полезные линки
  85.             http://www.javamadesoeasy.com/2015/06/differences-between-instance.html
  86.             http://stackoverflow.com/questions/15493189/beforeclass-vs-static
  87.  
  88.             Получим более содержательное сообщение об ошибке - если работаем с аннотированными методами
  89.             http://www.unknownerror.org/opensource/junit-team/junit/q/stackoverflow/512184/best-practice-initialize-junit-class-fields-in-setup-or-at-declaration
  90.             http://www.javaworld.com/article/2076265/testing-debugging/junit-best-practices.html
  91.             (подзаголовок Do not use the test-case constructor to set up a test case)
  92.  
  93. */
  94. ***************************************
  95. /*
  96.     Итак - есть у нас задача
  97.     теперь в пейджи нужно передать драйвер
  98.  
  99.     из пейджей доступаться до даных класса-предка тест-класса - это неверно
  100.  
  101.     т к пейдж - инструмент
  102.     он - вспомогательный
  103.     а когда инструмент просит что-то вспомогательное от сущностей, для которых он и создан -
  104.     это слишком запутано получается)
  105.  
  106.     самый простой спомоб - перейти от использования пейджей-модулей
  107.     к использованию пейджей-объектов
  108.     и в конструктор пейджа - передавать драйвер
  109.     и его внутри пейджа запоминать в спец поле
  110.  
  111.     и в методах пейджа - использовать вот этот вебдрайвер
  112.  
  113.     эта логика - будет характерна всем 3-м пейджам
  114.     значит - ее можно вынести на уровень предка пейджа
  115.  
  116.     реализуй BasePage
  117.     с конструктором и соответствующим полем - для вебдрайвера
  118.  
  119.     отнаследуй пейджи от этого класса
  120.     учти - что это пейжи-модули
  121.     вспомни про конвеншенсы для имен классов
  122.  
  123.     уже в Gmail - не нужно обїявлять и инициализиовать вебдрайвер
  124.     как и в других пейджах - будем инициализировать пейдж, передавая в его конструктор вебдрайвер из тест-класса
  125.  
  126.     и для всех методов пейджей - вебдрайвер из тест-класса станет доступным
  127.  
  128.     в самом тест-классе - инициализацию пейджей тоже поменяй - с учетом этих изменений
  129. */
  130. ***********************************************
  131. driver.get("https://gmail.com");
  132. /*
  133.     есть куча полезных вещей, которая нам бы пригодилась
  134.  
  135.     вот например open(String url) - универсальная и полезная функциональность
  136.  
  137.     будем ее віносить в отдельный класс
  138.  
  139.     пусть называется например ConciseAPI
  140.  
  141.     только и тут проблема - там тоже нету драйвера
  142.  
  143.     поступим пока не учень красиво зато сравнительно просто для понимания
  144.  
  145.     будем в каждый метод класса ConciseAPI там где требуется вебдрайвер - передавать вебдрайвер как параметр
  146.     потом это улучшим
  147.     а пока пусть так будет
  148.  
  149. */
  150. ***************************
  151.  driver.findElement(By.id("Email"))....
  152.  /*
  153.     есть у нас код - мы находим элемент и как-то с ним работаем
  154.  
  155.     но - мы при таком коде ровно в момент вызова кода - получаем элемент и с ним работаем
  156.     если на этот момент  - элемент не закгрузился (просто не успел)
  157.  
  158.     нам бы не помешал метод
  159.         который принимал бы локатор By
  160.         и возвращал вебэлемент
  161.  
  162.         и реализован был так -
  163.              сначала - ожидание видимости элемента
  164.              затем - его получение по локатору и возврат
  165.  
  166.         разумно назвать такой метод $
  167.              и реализовать в ConciseAPI
  168.  
  169.         и в пейджах - там где мы работаем с элементами - уже оперировать этим методом
  170. */
  171. *********************
  172. public static void send(String mail, String subject){
  173. /*
  174.     в  Selenide - ты использовала метод byText
  175.  
  176.     фактически - если посмотреть как он реализован - внутри он использует By.xPath
  177.  
  178.     посмотри на реализацию в  Selenide(на само xPath-выражение)
  179.     или сама выражение попроще примени
  180.  
  181.     реализуй в ConciseAPI - метод byText и тут используй
  182. */
  183. **********************************
  184.     public static void assertMails(String... emailTexts) {
  185.         for (int i = 0; i < emailTexts.length; i++) {
  186.             (new WebDriverWait(driver, 6)).until(
  187.                     textToBePresentInElementLocated(emails, emailTexts[i]));
  188.         }
  189.     }
  190. /*
  191.     для віполнения ждущей проверки - реализуй метод assertThat в ConciseAPI
  192.     код немного упростится -
  193.     вместо new WebDriverWait(driver, 6)).until(....)
  194.     будет  assertThat(driver, ....)
  195.  
  196.      и нужен новый кондишен )
  197.  
  198.                 у нас есть список мейлов.
  199.                 и есть текстЫ, которые должны содержать мейлы из списка
  200.                 вспомни эту задачу на селениде - как мы это проверяли
  201.  
  202.                 второй момент - кондишен и его параметр
  203.                 нам нужен аналог селенидовского texts
  204.                 причем такой кондишен должен работать для списка элементов
  205.                 (как и в предыдущем случае - реши самостоятельно - какого тапа будет параметр для списка элементов
  206.                  - List<WebElement> или By)
  207.  
  208.                 давай реализуем такой кондишен в CustomConditions
  209.  
  210. */
  211. ***************************
  212.     public static void assertMail(int index, String emailText) {
  213.         String cssSelectorForMail = "[role='main'] .zA:nth-child("+index+")";
  214.         (new WebDriverWait(driver, 6)).until(
  215.                 textToBePresentInElementLocated(By.cssSelector((cssSelectorForMail)), emailText));
  216.     }
  217. /*
  218.     и тут мы поступим также - воспользуемся assertThat(driver, ....)
  219.     и реализуем новый кондишен
  220.  
  221.     listNthElementHasText(final ... elements, final int index, final String expectedText)
  222.         который для проверит текст нн-ого элемента
  223.  
  224.         уже мы писали кондишены, правда попроще
  225.         теперь - усложним задачку
  226.  
  227.         но сначала давай обговорим тип первого параметра
  228.         мы можем написать кондишен для List<WebElement> elements
  229.         или для By elements
  230.  
  231.         если выбираешь первый вариант - пишем кондишен для лейзи прокси списка (полученного через FindBy)
  232.         написать и тот, и тот кондишен - примерно одинаково усилий нужно
  233.  
  234.         а вот чтобы вызвать кондишен  с List<WebElement> elements - тебе побольше придется постараться)
  235.         решай сама
  236.  
  237.         как написать такой кондишен - ниже подскажу
  238. */
  239.  
  240. /*
  241.  
  242.     Мы определились, что нам нужны 2 новых кондишена,
  243.     у которых  первый параметр - это List<WebElement> или By = список элементов,
  244.  
  245.         первый кондишен - textsOf(... elements, String... texts)
  246.             где параметры - список элементов (или его локатор) и текстЫ которые мы будем проверять для каждого из элементов
  247.  
  248.         второй кондишен - listNthElementHasText(.... elements, int index,  String text)
  249.             где параметры - список элементов(или его локатор), индекс проверяемого элемента
  250.             и текст который мы для него проверим
  251.  
  252.     чтоб не придумывать велосипед, давай посмотрим на код 2-ух стандартных селениумских кондишенов,
  253.     один из них работает со списком элементов и этим нам интересен
  254.     второй - с текстом и этим нам интересен
  255.  
  256.     стандартные кондишены реализованы в классе ExpectedConditions
  257.     напиши ExpectedConditions в своем коде
  258.     зажми ctrl+кликни на ExpectedConditions
  259.     откроется модуль и там можно будет посмотреть код кондишенов
  260.  
  261.     Попробуй по аналогии с приведенным примером организовать свои кондишены
  262.         У тебя есть лекция Якова, которая рассказывает, как кондишены устроены
  263.         У тебя есть пример кондишена с первым параметром такого же типа, как нам нужно
  264.         У тебя есть пример кондишена, который проверяет текст
  265.         Ты можешь открыть код селенидовского кондишена texts or exactTexts и тоже взять оттуда идеи
  266.         Мы определились с тем, что наши новые кондишены должны делать, с их именами и парамерами
  267.  
  268.     Попробуй написать эти 2 кондишена.
  269.  
  270.     Будет сложно - обращайся, будем упрощать задачу)
  271.  
  272.     Использовать List<WebElement> или By = список элементов = зависит от того,
  273.     будешь ли ты пейджи реализовывать с использованием @FindBy элементов
  274.     пока ты использовала локаторы By
  275.     мне тоже такой вариант кажется разумнее
  276.     можно на этом варианте и остановиться
  277. */
  278. *************************************
  279. By.cssSelector("[title='Sent Mail']")
  280. By.cssSelector("[title~='Inbox']")
  281. /*
  282.     и для таких вещей - можно реализовать методы byTitle / byExactTitle
  283. */
  284. ***********************************
  285. https://docs.google.com/document/d/1BiYTLdypDfucSqiY9isv1HCKKQIxelzqYrN-3Ku1RWM/edit?usp=sharing
  286. /*
  287.     вот это еще почитай
  288.     если сможешь разобраться - ок
  289.    
  290.     а нет - давай обсудим
  291.    
  292.     я ниже приведу текст по дженерик-типам - тоже надо бы разобраться
  293. */
  294. /*
  295.  
  296.   Кодишенов есть много и разных
  297.     и далеко не все они ExpectedCondition<Boolean>
  298.     вот в этих <...> - могут быть разные типы
  299.     если совсем грубо - то результат вот этого ... типа и вернет нам WebDriverWait(...).until(...) -
  300.  
  301.     например
  302.          есть такой кондишен ExpectedCondition<WebElement> visibilityOfElementLocated(By locator)
  303.          https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html#visibilityOfElementLocated-org.openqa.selenium.By-
  304.  
  305.          это значит WebDriverWait(...).until(visibilityOfElementLocated(...)) - вернет вебэлемент, видимость которого мы ждали
  306.  
  307.          а для кондишенов ExpectedCondition<Boolean> - new WebDriverWait(...).until вернет результат типа Boolean
  308.  
  309.          это не отменяет главной схемы работы - если проверка new WebDriverWait(...).until( - не проходит за таймаут -
  310.          то вызывается исключение
  311.  
  312.          но - помимо этого - есть вот такая разница ...
  313.  
  314.     пока наш assertThat возвращает void  - нас это может не сильно тревожить )
  315.     достаточно - в описании параметров убрать уточнение типа - не ExpectedCondition<Boolean>, а ExpectedCondition
  316.     и assertThat - будет работать с любым из кондишенов
  317.     правда - ничего нам не вернет)
  318.  
  319.     а вот если хочется получить результат new WebDriverWait(...).until( -
  320.     да еще и не городить кучу методов - придется подумать и немного теории почитать
  321.  
  322.     про это - в самом конце ревью
  323.     в принципе - пока - можно без этого обойтись
  324.     но в итоге - мы в любом случае это разберем, применим и освоим)
  325.    
  326.    
  327.     Давай разберемся. Сначала "идейно".
  328.    
  329.     Этот метод until возвращает такую интересную динамическую штуку-аватар...
  330.     "воплощение" которого зависит от того что именно ты передашь вейт антилу параметром
  331.     - точнее - какой именно кондишен ты ему передаешь...
  332.    
  333.     А кондишены бывают "разные"... "Разность" эта определеятся "параметром типа" который в джава записывается
  334.     ИмяТипа<ЗДЕСЬ>
  335.    
  336.     Ты уже ведь использовал списки, например...
  337.     В частности - List<WebElement>
  338.    
  339.     вот в такой записи типа - его параметром, выступает WebElement, указывая джаве,
  340.     что это не просто список, а "список ВебЕлементов"
  341.     а мог бы быть и списком стрингов например - List<String> или еще чего :)
  342.    
  343.     Такие "умные" типы которые получают параметр (или несколько параметров) - так и называются - параметризированными
  344.     (а фича языка, в которой они поддерживаются называется - параметрическим полиформизмом -
  345.     не путать с тем другим полиморфмизом... который называется - subtyping polymorphism,
  346.     а в джава все его знают как просто "полиморфизм")...
  347.     Еще они называются - шаблоны... или дженерики...
  348.     Именно так - Java Generics - их принято называть в Java
  349.    
  350.     Так вот... таким же дженериком является тип ExpectedCondition
  351.     который может быть параметризирован...
  352.    
  353.     Ты уже встречал запись типа:
  354.     ExpectedCondition<Boolean>
  355.    
  356.     что же значит этот булеан? что он параметризирует?
  357.     А он параметризирует внутреннюю реализацию этого кондишена...
  358.     Он определяет то, каким должен быть тип возвращаемого значения в методе apply
  359.    
  360.     То есть...
  361.     Если ты поставишь цель создать кондишен ExpectedCondition<Boolean> - ты должен будешь реализовать
  362.     его метод apply -  как возвращающий тип Boolean
  363.    
  364.     Но зачем вообще нам париться о типе возвращаемой сущности метода apply?
  365.    
  366.     А потому, что есть еще один "умный дженерик"...
  367.     Только который не "параметризированный класс", а "параметризированный метод"
  368.     (в джава бывают как Generic Types так и Generic Methods)
  369.    
  370.     и этот параметризированный метод - как раз и есть наш wait.until
  371.    
  372.     особенность реализации этого метода в том... что он вызывает внутри метод apply
  373.     нашего кондишена... и запоминает то значение, которое этот метод apply возвращает...
  374.    
  375.     И потом... в конце всей истории... этот метод until - либо бросит исключение
  376.     (в случае если "не дождется" кондишена)
  377.     либо вернет то, что вернул метод apply в "случае успеха"...
  378.    
  379.     Получается... Если ты передашь вейт антилу кондишен параметризированный типом Boolean
  380.     то в случае успеха антил - вернет тру...
  381.    
  382.     то есть ты можешь писать код вида:
  383.    
  384.     if (wait.until(enabled(composeButton))) {
  385.        doSomething();
  386.     }
  387.    
  388.     но на самом деле, такое нужно не часто...
  389.     то есть - такие кондишены - которые
  390.     параметризированы типом булеан...
  391.    
  392.     бошьше толку как раз от кондишенов параметризированных типом "того, характеристики чего ждет кондишен"...
  393.    
  394.     вот кондишен visibilityOf - как раз параметризирован типом WebElement
  395.         ExpectedCondition<WebElement>
  396.    
  397.     и его метод apply возвращает обьект типа WebElement,
  398.     а "идейно" - возвращает этот же элемент, визибилити которого мы дожидались...
  399.     (если бы мы не дождались - apply вернул бы null - что в этом случае играет роль "false")
  400.    
  401.     а until в конце концов вернет то, что вернет apply
  402.     и именно поэтому мы можем писать такой код:
  403.    
  404.         wait.until(visibilityOf(composeButton)).click()
  405.    
  406.     теперь твоим заданием будет - переделать assertThat что бы он возвращал то что возвращает wait.until
  407.    
  408.     ну и потом - переделать все свои кондишены, что бы они были параметризированы типом той сущности,
  409.     характеристики которой они должни дожидаться (кстати так выражатся немного некорректно - ведь кондишен не ждет,
  410.     он только выдает информацию - да/нет - а ждет уже вейт антил, ну то такое:) )
  411.    
  412.     для того, чтобы "правильно реализовать этот wait.until" -
  413.     тебе придется разобраться с этими дженериками...
  414.     И с дженерик типами и дженерик методами.
  415.    
  416.     И даже этого будет мало...
  417.     Прийдется покопаться в коде селениума, чтобы понять, что там за тип реально получает
  418.     вейт антил как кондишен...
  419.     Потому что он получает не ExpectedCondition а что то еще более странное...
  420.     Это работает - потому что это "странное" - есть родительским классом для ExpectedCondition
  421.    
  422.     */
  423.    
  424.     /*
  425.         Что почитать про Generics
  426.         про дженерики в общем(русский)
  427.         http://www.quizful.net/post/java-generics-tutorial
  428.    
  429.         http://www.tutorialspoint.com/java/java_generics.htm
  430.         http://developer.alexanderklimov.ru/android/java/generic.php
  431.    
  432.         конвеншенcы      http://stackoverflow.com/questions/2900881/generic-type-parameter-naming-convention-for-java-with-multiple-chars
  433.         https://docs.oracle.com/javase/tutorial/java/generics/types.html
  434.    
  435.         уроки
  436.         http://docs.oracle.com/javase/tutorial/extra/generics/index.html
  437.    
  438.         очень приличный faq (есть pdf, и есть кое-что еще, помимо дженериков)
  439.         http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
  440.    
  441.         ----------------------------------------------------------
  442.         для понимания кетчера (Catcher)
  443.         http://grepcode.com/file/repo1.maven.org/maven2/com.google.guava/guava/r06/com/google/common/base/Function.java
  444.         http://www.programcreek.com/java-api-examples/com.google.common.base.Function
  445.         https://docs.oracle.com/javase/tutorial/java/generics/lowerBounded.html
  446.         http://stackoverflow.com/questions/3847162/java-generics-super-keyword
  447.         http://stackoverflow.com/questions/2800369/bounding-generics-with-super-keyword
  448.         http://stackoverflow.com/questions/1910892/what-is-the-difference-between-super-and-extends-in-java-generics
  449.    
  450.         если осилишь после этого и применение кетчера(то, что в прошлом ревью отложили) - можно уже сейчас
  451.         по-прежнему - кетчера можно отложить
  452.     */
Advertisement
Add Comment
Please, Sign In to add comment