julia_v_iluhina

Untitled

Dec 26th, 2016
97
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 30.90 KB | None | 0 0
  1. review - http://pastebin.com/kgW1ArkG
  2.  
  3. 1. 'Используй не TestNG, а именно JUnit'
  4.     >>>>>>> не то чтоб я прям уверовал в JUnit, слишком плохо его знаю чтоб что то сравнивать наверно :)
  5. /*
  6.     ну, уверовать - большого смысла нету)
  7.     почитай материалы по линкам с прошлого ревью
  8.     если есть какие-то контраргументы - можно их обсудить
  9.  
  10.     это не вопрос веры, точно)
  11. */
  12. ********************************************************
  13. 2. 'Мне не кажется хорошей идея держать в проекте хромдрайвер как ресурс'
  14.     >>>>>>> мне кажется есть смысл хранить подобные штуки в проекте, т.к. он становится более мобильным.
  15.     для запуска таких тестов нужно минимально настроенное окружение, типа хрома и мавена.
  16.     например сейчас у меня тесты бегут параллельно на нескольких виртуалках, и мне не нужно думать о том,
  17.     что там с окружением у них может быть не так, хром обновляется автоматически и у меня локально и на виртуалках,
  18.     и если есть проблема в совместимости хромдрайвера с браузером - я узнаю об этом первым еще во время локального рана,
  19.     и пушну какие то последние фичи уже с хромдрайвером, который обновится автоматически из общего репозитория у всех слейвов.
  20.     но т.к. с новым селенидом у меня так и не получилось нормально фаерфокс запустить,
  21.     я даунгрейднулся до 3.11 и фф 47 и хрен с ним, возможность поставить хром вообще убрал с проекта)
  22. /*
  23.     представь, что на тех же виртуалках - крутятся еще с десяточек проектов)
  24.     и у каждого - внутри свой эксемпляр хромдрайвера - как ресурс
  25.  
  26.     а еще представь - что запускать нужно прект на разных операционках
  27.  
  28.     и все, хатка поломается)
  29.  
  30.     почитай побольше про воможные варианты
  31.     https://docs.google.com/document/d/1fodHkTunrtit-EiMBrb91Mc6rbnQ5LwtBL9rISQveKA/edit?usp=sharing
  32. */
  33. ***************************************************************
  34. 3. 'Тут пока применять пейджи - смысла большого не было
  35.        Эта задача эмулирует ситуацию когда мы пишем “драфт тест” - чтобы проверить как вообще автоматизация заходит на этот новый проект -
  36.        такой старт, потому наи пока и не нужны вспомогательные методы пейджа - степы
  37.        особенно - если учесть - что повторений практически нету'
  38.     >>>>>>>> мне просто так нравятся селенидовские пейджи, что я решил красивенько все сделать,
  39.      чтоб в тесте только тестовая логика была, без локаторов и т.п.
  40. /*
  41.     не убирай пейджа - он ОК ) упрощать - не будем
  42.    
  43.     ну, это как раз очень понятное желание) - сделать сразу красиво
  44.  
  45.     но  - при старте какого-то коммерческого проекта -
  46.     когда нас интересуют временнЫе затраты больше чем научные эксперименты
  47.     когда мы не уверены в выборе инструментов - разумно первый драфт тест реализовывать максимально просто
  48.     не тратясь на развитие красивой структуры проекта и не усложняя ничего раньше времени
  49.     чтоб наша оценка - подходит инструмент или нет - была максимально просто и быстро выполнена
  50.  
  51.     а дальше - если все ок - конечно есть смысл все красиво реализовывать
  52. */
  53. ******************************************************************
  54. 4.  'Это можно проще делать
  55.    у ElementsCollection - есть метод findBy
  56.    который в качестве параметра получает Condition'
  57.     >>>>>>>> спасибо за подсказку, но я так по пункту с KISS немного переделал страницу, поэтому теперь не получится :)
  58. /*
  59.     не поняла про  - по пункту с KISS
  60.     я писала
  61.     Ты много сущностей вынес в поля пейджа
  62.             Этим тоже не надо злоупотреблять,т к это не KISS
  63.             https://docs.google.com/document/d/13dNyFGbI7mV22UUhH8E0LJ7SzabAmX7Bw7VCHScYfiU/edit#heading=h.w4krdzi2dr1j
  64.  
  65.             правильнее создавать сущности - тогда, когда они нужны
  66.             и если какой-то локатор используется только в одном методе - то как правило - там ему и место
  67.  
  68.     что нам нужно в различных методах
  69.     именно - коллекция тасок
  70.     это = одна сущность
  71.     вот ее и стоило вынести в переменную ElementsCollection tasks = $$("#todo-list>li")
  72.     и далее - мы только с этой коллекцией и будем работать
  73.  
  74.     про - поэтому теперь не получится :)
  75.     получилось бы использовать tasks.findBy(exactText(text)) - если бы ты оперировал коллекцией
  76.     а не контейнером для коллекции
  77.  
  78.     да и такой вариант - tasksContainer.$$("li").findBy(exactText(text)) - тоже был бы ок
  79.     но - он хуже
  80.     т к нам нигде - не нужен этот контейнер без тасок - всегда нужна именно коллекция тасок
  81. */
  82.     private SelenideElement getTask(String text) {
  83.         for (SelenideElement taskContainer : tasksContainer.$$("li")) {
  84.             if (taskContainer != null && taskContainer.$("label").getText().equalsIgnoreCase(text))
  85.                 return taskContainer;
  86.         }
  87.         throw new IllegalArgumentException();
  88.     }
  89.  
  90.     //и
  91.  
  92.     private SelenideElement getTask(String text) {
  93.         return tasks.findBy(exactText(text));
  94.     }
  95.     /*
  96.         где tasks = $$("#todo-list>li")
  97.     */
  98. /*
  99.     Думаю, как раз второй вариант - гораздо проще
  100.  
  101.     taskContainer - мы к нему если и будем обращаться - то только
  102.     к его внутренним элементам - таскам
  103.     по-другому - не будем
  104.  
  105.     так зачем нам так реализованный taskContainer
  106.     если гораздо проще и полезнее - оперировать коллекцией tasks = $$("#todo-list>li")
  107.     ?
  108.  
  109.     Еще важный недостаток твоей реализации - помимо его сложности
  110.     мы - уже ищем элемент, уже перебираем коллекцию
  111.     уже анализируем тексты элементов
  112.     (тут конечно на такие грабли сложно наступить - но
  113.     в более сложных вариантах - можно будет запросто -
  114.     taskContainer.$("label").getText() - не ждет загрузки элемента, на момент выполнения этого кода -
  115.     текст элемента может еще недогрузиться)
  116.  
  117.     а вот такой вариант - tasks.findBy(exactText(text))
  118.     еще ничего не ищет вообще
  119.     мы лишь зафиксировали способ - как будем искать элемент
  120.     а искать мы его будем - когда будем с ним работать
  121.         то ли действия выполнять
  122.         то ли проверять
  123.     перед действиями - будет выполнено ожидание видимости элемента (в рамках таймаута)
  124.     проверка - тоже ждущая
  125.     в рамках любой ждущей проверки что происходит
  126.         в цикле, пока не истечет таймаут или пока не будет выполнено условие проверки
  127.          выполняется поиск элемента(или элементов)
  128.          потом - проверка
  129.          если все ок - уже прекращаем процесс
  130.          если не ок - ждем небольшой интервал(сколько-то миллисекунд)
  131.          и снова пробуем выполнить это же
  132.          и так пока не истечет таймаут или не будет выполнено условие
  133.     получается - что используя ждущие проверки - мы достаточно надежно проверим - даже если страница догружается постепенно
  134.     и не только проверим - но и начнем работать с элементом, не раньше чем он станет видимым
  135.     (ожидание видимости уже встроено в любое из действий над элементом - ведь нету смысла с элементом работать - пока он невидим)
  136.  
  137.     это если говорить о том - что under the hood
  138.  
  139.     а если смотреть с внешней стороны - то одна строка кода - всегда проще нескольких
  140.     особенно - которые еще и с циклом, if-ом, и исключениями )
  141.  
  142. */
  143. *******************************************************************************************************
  144. 5. 'не знаю, стоило ли объединять создание пейджа и открытие урла'
  145.     >>>>>>>> взял из примеров с селенид сайта
  146.     https://github.com/selenide-examples/google/blob/master/test/org/selenide/examples/google/selenide_page_object/GoogleTest.java
  147.     но переделал :)
  148. /*
  149.     не знаю, видел ли ты вот эту статью
  150.     http://automation-remarks.com/2016/selenide-shadow-sides/index.html
  151.     особенно ценны - комментарии Солнцева (автора Selenide) к ней
  152. */
  153. ************************************************************
  154. 6. 'создание пейджа - я бы делала вне тест-метода'
  155.     >>>>>>>> а где? в бефоре нельзя, статики я не хотел делать.
  156. /*
  157.     чего нельзя? вполне можно)
  158.     и вариантов есть поболее чем один бифор)
  159.     еще и instance initialisation block вполне ок будет
  160.     просто - нужно на уровне класса объявить переменную для пейджа
  161.     а уже в бифор-методе или instance initialisation block - уже ее инициализировать
  162.  
  163.     да и в статиках - ничего плохого нет)
  164.     важно - уместно их применять
  165.     можно благодаря этому кое-что пооптимальнее реализовать
  166.     ну то разговор не на сейчас)
  167.  
  168.     Selenide - уже использует статический драйвер…
  169.     и наша пейджа селенидовская - уже по факту “статическая”
  170.     в контесте запуску тестов в браузере
  171.     потому создавать ее не статической - в общем-то - обманывать себя )
  172.  
  173.     или вообще в момент объявления поля - пейджа - как ты и сделал
  174.     самый лаконичный вариант
  175.  
  176.     JUnit для запуска каждого теста заново создает объект тест-класса
  177.     в этот момент - будут созданы и все поля класса (не статические)
  178.     так что все ок
  179.  
  180.     http://www.javamadesoeasy.com/2015/06/differences-between-instance.html
  181. */
  182. **********************************************************
  183. 7. 'где в проекте располагается этот класс  (ветка проекта src\main - т к вещь это = универсальная)'
  184.     >>>>>>>> я честно говоря вообще не понимаю к чему эти разделения на main и tests с ограничениями видимости, они мешают.
  185.     почему не делать все в main, а уже в нем сделать разделение на tests, pages, elements, utils и так далее
  186. /*
  187.     все же порядок и какие-то договоренности - всегда лучше
  188.     чем решения, принятые в одно лицо, исходя из индивидуальных предпочтений и чувства прекрасного )
  189.  
  190.     ведь практически не бывает того, чтоб ты использовал тесты - сам написал, и сам используешь
  191.     и никто, кроме тебя, на них не смотрит и с этим кодом не работает
  192.  
  193.     так или иначе - мы - командные игроки)
  194.     добавь к этому - что никто в команде - не навсегда
  195.     и хорошо бы после себя оставить такой код, в который будет сравнительно просто въехать
  196.  
  197.     предсказуемая структура, выполненные конвеншенсы - очень способствуют более быстрому разбору чужого кода
  198.     такая отправная точка
  199.  
  200.     но это только верхушечка айсберга)
  201.     попробуй  - запусти тесты, которые лежат  в ветке src\main
  202.     или тест-методы из тест-классов, имя которых заканчивается не на Test
  203.     с командной строки попробуй)
  204.     получилось? ))
  205.  
  206.     а ограничения видимости - они на самом деле - не мешают
  207.     а не допускают ошибок
  208.  
  209.     пример
  210.     в src\main - расположили пейджи - как вещь универсальную и переиспользуемую
  211.     также в src\main - расположили некие универсальные методы в классе-хелпере
  212.  
  213.     если пытаться из этих универсальных классов - обратиться к содержимому классов из src \ test - ничего не получится
  214.     и это хорошо и правильно
  215.     т к - инструменты служат тест-классам
  216.     а не тест-классы- инструментам
  217.  
  218.     аналогично - если речь идет о классах с тестовыми данными (такие должны находиться в src \ test)
  219.     это очень хорошо - что к таким классам невозможно будет из пейджа обратиться
  220.     т к в таком случае - пейдж не будет завязан на какие-то конкретные тестовые данные
  221.     все тестовые данные будут получены методми пейджа - как значения параметров
  222.     и тест-метод  - будет проще читаться, не будет магии
  223.     и пейджи - будут максимально универсально реализованы - их можно будет более разнообразно использовать при развитии проекта
  224.     думаю, этот момент со временем тоже станет понятнее
  225.    
  226.         ну и вообще)
  227.         так оно так и есть как ты говоришь, только в мавене твой main называется src
  228.         и в нем уже два больших шкафа - с тестами, и со всем остальным
  229.         или даже один шкаф - src, просто с двумя отделениями)
  230.         которые мы еще будем впоследствии делить - будет куча вложенных делений
  231.         и это нормально - отделить все, что касается тестов, от остального
  232.    
  233.         в принципе - src \ main - можно вообще не использовать
  234.         туда кладут core фреймворка, который будет переиспользоваться или сам продукт
  235.         (бывает что продукт и его тесты - живут в одном проекте)
  236.         и по большому счету - такое распределение - именно для этого и нужно
  237.    
  238.         также - пейджи - туда тоже кладут (если продукт не живет вместе с тестами в одном проекте - так получится неаккуратно)
  239.         если в проекте только тесты (дальше - буду приводить пример структуры проекта, из расчета что продукт и тесты живут в разных проектах)
  240.         и это на самом деле - защита от дурака
  241.         т к нельзя к тестовым данным подрубиться из пейджа
  242.         да и к самим тест-классам
  243.         (из кода src \ main - не видно ничего из src\test)
  244.         а из моего опыта - таких умников достаточно - которые так пытаются делать
  245.  
  246. */
  247. ******************************
  248. public void endToEnd() {
  249. /*
  250.     почитай вот эти советы
  251.     https://docs.google.com/document/d/13dNyFGbI7mV22UUhH8E0LJ7SzabAmX7Bw7VCHScYfiU/edit#bookmark=id.2gjiy0o1o48q
  252.  
  253.     и подправь имя тест-метода
  254.  
  255.     endToEnd - совсем не информативно
  256.     варианты tasksLifeCycle, tasksCommonFlow
  257.     дадут нам понять - что это е2е тест, причем тестирует он - именно работу с тасками
  258.     т е - да, мы по-прежнему не уточнились до конкретных покрытых действий (т к получится днинно)
  259.     но - все равно - мы точнее - чем в варианте  endToEnd
  260. */
  261. **************************
  262.  page.addTask....
  263.                 .tasksShouldHaveSize(4);
  264. /*
  265.     после добавления тасок - проверки размера списка - недостаточно
  266.     т к - могли таски добавиться - и не с теми текстами, и не в той последовательности
  267.     для коллекции элементов - есть кондишен exactTexts
  268.  
  269.     нам достаточно проверки shouldHave(exactTexts("...", "...", ...))
  270.                 как осуществяется проверка по кондишену exactTexts
  271.                 сверяется количество, порядок и тексты
  272.                     количество элементов коллекции должно быть равно количеству переданных текстов
  273.                     иначе - проверка не прошла
  274.                     и далее - по порядку сверяются текст элемента и переданный текст
  275.                     нулевой - с нулевым
  276.                     первый с первым
  277.                     и  т д
  278.                 таким образом - уже не нужно проверять размер списка
  279.                 раз уже проверили  exactTexts
  280.     касается всех реализованных проверок
  281. */
  282. ***************************
  283. public class BaseTest {
  284.  
  285.     @BeforeClass
  286.     public static void beforeClass() {
  287.         Configuration.browser = "firefox";
  288.         Configuration.reportsFolder = "target" + separator + "screens";
  289.         Helper.setDriver(getWebDriver());
  290.     }
  291.  
  292. }
  293. /*
  294.     по умолчанию - и так будет использован firefox
  295.     переназначать Configuration.reportsFolder - не могу предположить даже, какая от этого может быть польза)
  296.  
  297.     в самом Helper - можно просто получить getWebDriver()
  298.     и не создавать ни дополнительного поля - для хранения драйвера
  299.     в рамках селенидовского проекта (любого) - getWebDriver() - вернет тебе веблрайвер для текущего потока
  300.     так что все ок будет
  301.  
  302.     да и таймаутом можно пользоваться селенидовским)
  303.     Confifuration.timeout (учти что он задан в миллисекундах)
  304.     но то уже так, на твое усмотрение
  305.  
  306. */
  307. ******************************************
  308. new WebDriverWait(driver, 4).until(Conditions.waitUntilAjaxCompleted);
  309. //или
  310. new WebDriverWait(driver, 4).until(waitUntilAjaxCompleted);
  311. //или
  312. new WebDriverWait(driver, 4).until(ajaxCompleted);
  313. /*
  314.     используй import static и переименуй кондишен
  315.     ведь сам кондишен - ничего не ждет
  316.     это значение параметра для нашего ожидания
  317.  
  318.     также советую переименовать класс  Conditions на CustomConditions
  319.     и дать имя классу Helper - более точное (если он будет служить исключительно контейнером для ожидания загрузки Ajax
  320.  
  321.     а если Helper - будет контейнером для разнообразных универсальных методов
  322.     то стоит назвать его Helpers
  323.     и оставить там - лишь вспомогательные методы
  324.     убрать какую-то общую логику для класса
  325.     это сделать просто
  326.      достаточно убрать поля в классе и их сеттеры
  327.      и реализовать
  328.      public static void waitUntilAjaxComplete(long timeout)
  329.      public static void waitUntilAjaxComplete() - тут - вызываем waitUntilAjaxComplete(long timeout)
  330.      с использованием селенидовского таймаута
  331.      т е - если нужно указать явно таймаут - вызовем первый
  332.      а если можно не уточняться и значение Configuration.timeout нас устраивает - то и ок - вызовем второй
  333.  
  334.     однозначно - эти 2 класса (Condition & Helper) - должны жить в ветке src \ main
  335.  
  336.     приведу сразу - пример удачной структуры проекта
  337. */
  338. /*
  339.     http://joxi.ru/nAyqEx7HXvxQoA
  340.  
  341.     вот пример хорошей структуры проекта
  342.  
  343.     в src \ main
  344.  
  345.       core - универсальное, что можно переиспользовать в разных проектах
  346.       pages - пейджи тоже можно переиспользовать для других тестов этого же приложения
  347.  
  348.  
  349.     в src \ test
  350.  
  351.       testdata - тестовые данные (если такие есть и они вынесены в отдельный класс)
  352.       testconfigs - предки тест-класса (так можно их изолировать от  собственно тест-классов - чтоб легче было ориентироваться
  353.  
  354.  
  355.     про пекеджи еще немного)
  356.     если GroupID = com.somesite
  357.     а проект todomvctest
  358.     то пакет корневой должен быть com.somesite.todomvctest
  359.  
  360.     логика  - чтобы "не смешивались имена сущностей"
  361.  
  362.     внутри одной компании - может быть несколько проектов)
  363.     и у всех у них один com.somesite  - базовый пекедж
  364.     но для каждого проекта должен быть свой  “базовый пекедж проекта"
  365.     иначе все смешается)
  366.     важно то, что когда этот проект выльется в отдельную библиотеку,
  367.     то не будет конфликтов при его подключении
  368. */
  369. *************************************
  370. public class MainPage {
  371. /*
  372.     если пейдж единственный - я бы назвала его TodoMvcPage - все равно точнее будет - что за пейдж
  373.     не настаиваю
  374. */
  375.     private SelenideElement tasksContainer = $("#todo-list");
  376. /*
  377.     писала выше - нам от #todo-list - нужны только и только таски
  378.     так почему сразу - не оперировать коллекцией $$("#todo-list>li") ?
  379.  
  380. */
  381. ***************************************
  382.  
  383.     public MainPage tasksShouldHaveSize(int quantity) {
  384.         try {
  385.             tasksContainer.$$("li").shouldHaveSize(quantity);
  386.             return this;
  387.         } catch (NoSuchElementException e) {
  388.             if (quantity == 0) return this;
  389.             throw e;
  390.         }
  391.     }
  392. /*
  393.     try-catch - блока не нужно
  394.  
  395.     проверка shouldHaveSize если и вызовет исключение - то это значит - проверка не прошла
  396.     и это ок - что тест упадет
  397.  
  398.     да и исключение будет никак не NoSuchElementException
  399.     а потомка UIAssertionError (https://github.com/codeborne/selenide/blob/master/src/main/java/com/codeborne/selenide/ex/UIAssertionError.java)
  400.     точно ловить ничего не нужно
  401.     и тем более = как-то обрабатывать вариант когда quantity == 0
  402. */
  403. ************************************************************
  404.     public MainPage completedTasksShouldHaveSize(int quantity) {
  405.         try {
  406.             tasksContainer.$$(".completed").shouldHaveSize(quantity);
  407.             return this;
  408.         } catch (NoSuchElementException e) {
  409.             if (quantity == 0) return this;
  410.             throw e;
  411.         }
  412.     }
  413. /*
  414.     про try-catch  и обработку quantity == 0 - все то же самое
  415.  
  416.     если мы оперируем коллекцией тасок tasks = $$("#todo-list>li")
  417.     то тут ее - просто отфильтруй по классу completed
  418.     есть такой метод для коллекции - filter
  419.     который оперирует значением типа Condition
  420.     и есть кондишен cssClass, который в качестве параметра - принимает имя класса
  421. */
  422. *******************************************************************
  423.     public MainPage addTask(String text) {
  424.         $("#new-todo").setValue(text);
  425.         $("#new-todo").pressEnter();
  426.         return this;
  427.     }
  428. /*
  429.     можно переписать в одну строку - $("#new-todo").setValue(text).pressEnter();
  430. */
  431. ***********************************************
  432.     public MainPage toggleTask(String text) {
  433. //сравни
  434.     public MainPage toggle(String taskText) {
  435. /*
  436.     поскольку все действия - выполняем над тасками - можно
  437.     в именах методов-действий - убрать вот это уточнение Task
  438.  
  439.     и уточнить это уже в имени параметра
  440.     получишь в итоге - более аконичный код в тест-методе
  441.     причем - наглядности и точности не потеряешь
  442.  
  443.     такое решение нужно распростарнить на все методы пейджа
  444.     которые служат для выполнения действий
  445.  
  446.     для методов-проверок - не стоит сокращать имена
  447.     тут лучше явно все описать - что мы проверяем
  448.     а то будет не очевидно
  449. */
  450. ******************************
  451.     private SelenideElement getTask(String text) {
  452.         for (SelenideElement taskContainer : tasksContainer.$$("li")) {
  453.             if (taskContainer != null && taskContainer.$("label").getText().equalsIgnoreCase(text))
  454.                 return taskContainer;
  455.         }
  456.         throw new IllegalArgumentException();
  457.     }
  458. /*
  459.     писала выше про недостатки такого метода
  460.  
  461.     эта версия - неоправданно сложная и не стабильная
  462. */
Advertisement
Add Comment
Please, Sign In to add comment