julia_v_iluhina

Untitled

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