julia_v_iluhina

Untitled

Feb 15th, 2017
110
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 19.92 KB | None | 0 0
  1.      <build>
  2.         <plugins>
  3.             <plugin>
  4.                 <groupId>org.apache.maven.plugins</groupId>
  5.                 <artifactId>maven-compiler-plugin</artifactId>
  6.                 <configuration>
  7.                     <source>1.7</source>
  8.                     <target>1.7</target>
  9.                 </configuration>
  10.             </plugin>
  11.             <plugin>
  12.                 <groupId>org.apache.maven.plugins</groupId>
  13.                 <artifactId>maven-surefire-plugin</artifactId>
  14.                 <version>2.19.1</version>
  15.                 <configuration>
  16.                     <parallel>classes</parallel>
  17.                     <threadCount>4</threadCount>
  18.                 </configuration>
  19.             </plugin>
  20.         </plugins>
  21.     </build>
  22. /*
  23.     В ConciseAPI - оставь реализованное для параллелизации
  24.     а тут - предлагаю закомментить
  25.     и в BaseTest - перейти на вариант создания и удаления вебдрайвера - в BeforeClass & AfterClass
  26.  
  27.     цель - оставить механизмы для параллелизации
  28.     и при этом - отлаживаться более удобно
  29.  
  30.     на твое усмотрение
  31.     просто совет - как будет удобнее работать
  32.  
  33.     еще один совет
  34.     см https://docs.google.com/document/d/1fodHkTunrtit-EiMBrb91Mc6rbnQ5LwtBL9rISQveKA/edit?usp=sharing
  35.     там есть информация - как использовать Selenium 3.0.1
  36.     для фф<=47.0.1
  37. */
  38. **************************
  39. .pollingEvery(500, TimeUnit.MILLISECONDS)
  40. /*
  41.     Этот таймаут можно вынести в Configuration - отдельной настройкой
  42.     это даст дополнительные возможности для настройки
  43.  
  44.     сразу подумай - в каких единицах хранить оба таймаута
  45.     цель - простота и единообразие
  46. */
  47. *********************************
  48. public interface OverriddenCondition<T> extends Function<By, T> {
  49. }
  50. /*
  51.     логику выбора имени - OverriddenCondition
  52.     понимаю)
  53.  
  54.     но - давай взглянем на это со стороны
  55.     что нам для понимания дает слово Overridden?
  56.  
  57.     предлагаю оперировать именем Condition<T>
  58.     оно ничуть не менее информативно)
  59. */
  60. ********************************************
  61. public static OverriddenCondition<WebElement> isVisible() {
  62.         return new OverriddenCondition<WebElement>() {
  63.  
  64.             WebElement element;
  65.             /*
  66.                 вспомни ранее реализованные кондишены
  67.                 мы на уровне класса - объявляли переменные, в которых сохраняли actual result
  68.                
  69.                 ведь в toString - мы описываем - expected & actual результаты
  70.                 мы ожидаем - что элемент будет видим
  71.                 а фактический результатт - это не element, а element.isDisplayed()
  72.                 именно это мы анализируем
  73.                  
  74.                 мне таймаута в 10 секунд - не хватает
  75.                  и тест падает с Null pointer Exception
  76.                  
  77.                 т к на момент вызова  toString() этого кондишена - такой эксепшен возникает при вызове element.toString()
  78.                 думаю, если ты уменьшишь таймаут - ты сможешь смоделировать эту проблему
  79.                
  80.                 в любом случае - сообщение об ошибке должно быть полезным
  81.                 так что - советую проверить в этом режиме
  82.             */
  83.             By locatorToString;
  84.             /*
  85.                 Имя locator - более точное в данном случае
  86.             */
  87.            
  88. ******************************************
  89. /*
  90.     хорошая идея - реализовать умное ожидание в отдельном классе
  91.     с точки зрения Single Responsibility - так будет лучше
  92.  
  93.     наглядно этот класс назвать WaitFor
  94.     а статический метод-умный ожидатель выполнения кондишена - until
  95.     и в коде мы будем писать waitFor(byCss(...)).until(visible())
  96.     получится очень наглядная и понятная фраза
  97.  
  98.     чтобы так писать - waitFor(byCss(...)).until(visible())
  99.         реализуй конструктор с параметром By locator
  100.  
  101.         реализуй статический метод waitFor с параметром By locator
  102.         возвращающий - новый объект класса WaitFor
  103.  
  104.         реализуй метод until(...)
  105.  
  106.     получишь
  107.        waitFor(byCss(...)). - создали объект типа WaitFor
  108.           until(visible()) - вызвали у него метод until
  109.  
  110.     конечно, until-у передаай как кондишен, так и таймаут
  111.     уже второй вариант  until-а - без таймаута (также как и раньше - такой берет таймаут по умолчанию
  112.  
  113.     сразу стоит реализовать вариант
  114.     until(Condition<V>... conditions)
  115.     как более универсальный
  116.     как это работает - поочередно выполняем ждущую проверку для всех переданных кондишенов
  117.     до тех пор пока какая-то проверка не упадет
  118.     что возвращаем - результат последней ждущей проверки
  119.  
  120.     сама реализация ждущей проверки (с помощью FluentWait) - это ок
  121.     просто - реализуй специальный класс для ждущей проверки
  122.  
  123.     тут цель - Single Responsibility Principle
  124. */
  125. ************************************************
  126. /*
  127.     Сейчас кондишены реализованы как объекты анонимных классов
  128.     Мы сможем больше пользы получить - если будем реализовывать каждый кондишен как класс имплементирующий интерфейс
  129.         Condition<WebElement>
  130.         или
  131.         Condition<List<WebElement>>
  132.  
  133.     Иерархия для кондишенов все еще будет достаточно простая
  134.  
  135.     интерфейс
  136.     и классы-кондишены, имплементирующие его
  137.  
  138.     Минимально необходимая реализация есть
  139.  
  140.     Будем ее развивать
  141.  
  142.     Далее - реализуй абстрактный класс-предок AbstractCondition<T> ,
  143.     который имплементирует интерфейс Condition<T>
  144.  
  145.     Статья про нейминг
  146.     http://www.vertigrated.com/blog/2011/02/interface-and-class-naming-anti-patterns-java-naming-convention-tautologies/
  147.  
  148.     Теперь смотрим дальше
  149.     Нам хочется, чтобы наш toString() для любого кондишена был хорошо структурирован
  150.     Это значит, чтобы фразы были типа
  151.  
  152.           __имя кондишена__ +
  153.           for ____element/elements___ found by: ___locator___
  154.           expected ___expected result description_____
  155.           actual ____actual result description_____
  156.  
  157.     Можно конечно в каждом из кондишенов просто строить такую фразу
  158.     согласно правилу
  159.  
  160.     А можно - toString() реализовать в абстрактном классе, и внутри этого метода - вызывать абстрактные методы
  161.     а их уже - реализуем в потомках. Таким образом - структура сообщения любого из кондишенов
  162.     будет одинаковой
  163.  
  164.     где брать ___locator___ тут - мы чуть позже поговорим...
  165.     пока пропусти этот момент
  166.  
  167.     а вот для
  168.         ____element/elements___
  169.         ___expected result description_____
  170.         ____actual result description____
  171.     объяви и используй абстрактные методы
  172.         identity()
  173.         expected()
  174.         actual()
  175.  
  176.     __имя кондишена__
  177.     раз мы будем реализовывать кондишены как отдельные классы - потомки нашего абстрактного класса
  178.     то откуда взять имя кондишена - у нас есть
  179.     у нас есть имя класса кондишена - его можно использовать
  180.  
  181.     Далее
  182.     element/elements
  183.     Это только верхушечка айсберга
  184.     Да, схема проверки кондишенов - у нас одна и та же
  185.     Хоть это кондишен для списка элементов
  186.     Хоть для элемента
  187.     И нам нужно, чтоб наш waitUntil был один и работал с любым кондишеном
  188.  
  189.     Но
  190.     Нам важна разница - что это за кондишен - для элемента или коллекции
  191.     В данный момент - чтобы сказать про то в фразе - for ____element/elements
  192.     А далее (в следующих заданиях) - чтобы кондишены для element - мы просто  НЕ МОГЛИ применить к elements
  193.     Таким образом, приходим к тому, что у нашего общего предка AbstractCondition<V>
  194.     Будут 2 абстрактных потомка
  195.         CollectionCondition extends AbstractCondition<List<WebElement>>
  196.         ElementCondition extends AbstractCondition<WebElement>
  197.     От которых мы будем наследовать наши реальные кондишены
  198.     Потом это мы будем менять, но пока - это будет полезно
  199.  
  200.     И тогда, стоит держать 2 класса для реализации статических методов - вызовов кондишенов
  201.     CollectionConditions и  ElementConditions
  202.         это будут классы-контейнеры статических методов
  203.         типа
  204.         public static Condition<WebElement> visible() {
  205.             return new Visible();
  206.         }
  207.         чтобы уже в коде  - не писать - new Visible()
  208.  
  209.     С иерархией разобрались немного )
  210.  
  211.     Возвращаемся к фразе в toString()
  212.         Если этот метод реализовать в главном предке AbstractCondition,
  213.         и этот метод будет использовать абстрактные методы, возвращающие строки
  214.         для всех элементов этой фразы
  215.         То структура сообщения о кондишене - будет всегда четкой
  216.  
  217.     А нам останется - реализовать эти абстрактные методы, возвращающие строки
  218.     в потомках
  219.     Тут смотри внимательно
  220.     Некоторые из методов надо будет реализовать уже в кондишенах-потомках
  221.     Некоторые - в CollectionCondition и ElementCondition
  222.  
  223.     Руководствуйся такими рассуждениями
  224.     Если во всех потомках я пишу одинаковый код
  225.     То лучше этот код написать в предке
  226.  
  227.     Что касается типа параметра condition для
  228.     until(Condition<V> condition)
  229.  
  230.     Ты можешь указать интерфейс Condition<V>
  231.  
  232.     И метод будет корректно работать для любого из кондишенов
  233.     Т к этот интерфейс лежит в основе этой иерархии
  234. */
  235. /*
  236.     еще один наворот внутри кондишенов)
  237.  
  238.     мы его впоследствии переделаем
  239.     сейчас нам это нужно для лучшего понимания процесса
  240.  
  241.     в apply передают локатор
  242.     описание которого мы возвращаем в одном из методов кондишена (для ToString)
  243.     и это - справедливо для всех кондишенов
  244.  
  245.     и нам - еще в AbstractCondition интересно сохранить этот локатор
  246.  
  247.     потому в AbstractCondition заводим переменную locator
  248.     и реализуем метод apply
  249.     в котором
  250.         сохраняем переданный в apply локатор
  251.         и вызываем абстрактный метод check
  252.         (- который, собственно, и нужно будет в кондишенах реализовывать вместо apply)
  253.  
  254.     check - будет делать то же, что и  apply делал ранее
  255.     нам  это нужно было для того, чтоб локатор получить еще на уровне абстрактного класса
  256.  
  257.     теперь - и в toString - мы можем подправить фразу - про локатор
  258.  
  259.     сразу подумай, какой модификатор доступа использовать для такой переменной
  260.     http://www.quizful.net/interview/java/access-modifiers
  261.  
  262.     но это опять была только верхушка айсберга)
  263.  
  264.     посмотри на реализованные кондишены
  265.     первой строкой в методе проверок - у тебя идет
  266.     List<WebElement> elements = getDriver().findElements(elementsLocator);
  267.     или
  268.     WebElement element = getDriver().findElement(elementsLocator);
  269.     (таких, кстати, пока нету
  270.         реализуй
  271.         visible
  272.         present
  273.         text
  274.         exactText
  275.     )
  276.  
  277.     Так вот
  278.     фактически - вот это
  279.     List<WebElement> elements = getDriver().findElements(elementsLocator);
  280.     или
  281.     WebElement element = getDriver().findElement(elementsLocator);
  282.  
  283.     это сущность типа V с точки зрения класса AbstractCondition
  284.  
  285.     давай метод check изменим
  286.     пусть принимает не локатор
  287.     а уже V entity (т е внутри кондишена в реализации check
  288.     нам ничего не придется искать по локатору - нам это уже на вход передадут)
  289.  
  290.     а вот в реализации apply в AbstractCondition
  291.     для получения этого V entity
  292.     мы объявим новый абстрактный метод getWrappedEntity()
  293.     который будет нам возвращать значение типа V
  294.  
  295.     а реализуем его в классах CollectionCondition и ElementCondition
  296.  
  297.     думаю, ты догадаешься, как )
  298.  
  299.     а вот потом, когда у нас будут наши классы для элементов (следующее задание)
  300.     мы будем в apply передавать не локатор
  301.     а сам элемент нашего нового класса
  302.     и сам элемент будет возвращать getWrappedEntity()
  303.     и мы тут, в кондишенах, избавимся от  getWrappedEntity() и его реализаций )
  304.  
  305.     пока это такой костыль
  306.     нарушающий принцип Single Responsibility
  307.     но позволяющий нам сейчас максимально реализовать код
  308.     который впоследствии изменим минимально
  309. */
  310. /*
  311.     ну и еще один наворот на тему интерфейсов )
  312.  
  313.     как выше писала - класс может имплементировать несколько интерфейсов
  314.  
  315.     и прикольно - всю функциональность описывать на уровне интерфейсов
  316.     это дает ряд интересных полезных эффектов
  317.  
  318.     вот тут например -
  319.     описать интерфейс DescribesResult
  320.     с методами expected() и  actual()
  321.  
  322.     и пусть класс AbstractCondition<V> имплементирует как Condition<V>, так и DescribesResult
  323.  
  324.     первая мелочь - можно описания абстрактных методов отсюда убрать
  325.     (они же и так объявлены в интерфейсе, а реализовывать мы будем в кондишенах уже)
  326.  
  327.     вторая приятная и важная вещь - соблюли лучше Single Responsibility Principle
  328.  
  329.     третья приятная вещь
  330.     конечно, при условии, если таки для переменных-кондишенов будем использовать типы интерфейсов
  331.     Condition<...>
  332.     когда мы будем оперировать переменной
  333.         Condition<...>  condition
  334.  
  335.     то для такой переменной - мы не сможем вызвать методы  expected() /   actual()
  336.     т е - там, где это было нужно - у нас эта функциональность была видна
  337.     а там, где это не нужно - этого вообще не видно
  338. */
  339. ********************************************
  340. public static Condition<WebElement> visible() {
  341.         return new Visible();
  342. }
  343. /*
  344.     по идее - можно было еще короче)
  345. */
  346. public static Condition<WebElement> visible = new Visible();
  347.  
  348. /*
  349.     т е - реализовывать не метод, а статическую переменную
  350.     вроде бы - лаконичнее, что хорошо
  351.  
  352.     но - не так уж и хорошо)
  353.  
  354.     Тут есть такая тонкость)
  355.  
  356.     мы создали статические переменные = объект-кондишен
  357.  
  358.     и используем их в проекте, который поддерживает параллелизацию
  359.  
  360.     если мы в параллельных тестах будем использовать ОДИН объект-кондишен ОДНОВРЕМЕННО
  361.     могут возникать странные ошибки
  362.  
  363.     чтобы избежать этого
  364.     вместо переменных стоит использовать методы
  365.     тогда для каждого вызова кондишена мы будем использовать свой объект
  366.     и таких проблем не будет
  367.  
  368.     наш текущий вариант с методом - грамотнее)
  369. */
Advertisement
Add Comment
Please, Sign In to add comment