Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>1.7</source>
- <target>1.7</target>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <version>2.19.1</version>
- <configuration>
- <parallel>classes</parallel>
- <threadCount>4</threadCount>
- </configuration>
- </plugin>
- </plugins>
- </build>
- /*
- В ConciseAPI - оставь реализованное для параллелизации
- а тут - предлагаю закомментить
- и в BaseTest - перейти на вариант создания и удаления вебдрайвера - в BeforeClass & AfterClass
- цель - оставить механизмы для параллелизации
- и при этом - отлаживаться более удобно
- на твое усмотрение
- просто совет - как будет удобнее работать
- еще один совет
- см https://docs.google.com/document/d/1fodHkTunrtit-EiMBrb91Mc6rbnQ5LwtBL9rISQveKA/edit?usp=sharing
- там есть информация - как использовать Selenium 3.0.1
- для фф<=47.0.1
- */
- **************************
- .pollingEvery(500, TimeUnit.MILLISECONDS)
- /*
- Этот таймаут можно вынести в Configuration - отдельной настройкой
- это даст дополнительные возможности для настройки
- сразу подумай - в каких единицах хранить оба таймаута
- цель - простота и единообразие
- */
- *********************************
- public interface OverriddenCondition<T> extends Function<By, T> {
- }
- /*
- логику выбора имени - OverriddenCondition
- понимаю)
- но - давай взглянем на это со стороны
- что нам для понимания дает слово Overridden?
- предлагаю оперировать именем Condition<T>
- оно ничуть не менее информативно)
- */
- ********************************************
- public static OverriddenCondition<WebElement> isVisible() {
- return new OverriddenCondition<WebElement>() {
- WebElement element;
- /*
- вспомни ранее реализованные кондишены
- мы на уровне класса - объявляли переменные, в которых сохраняли actual result
- ведь в toString - мы описываем - expected & actual результаты
- мы ожидаем - что элемент будет видим
- а фактический результатт - это не element, а element.isDisplayed()
- именно это мы анализируем
- мне таймаута в 10 секунд - не хватает
- и тест падает с Null pointer Exception
- т к на момент вызова toString() этого кондишена - такой эксепшен возникает при вызове element.toString()
- думаю, если ты уменьшишь таймаут - ты сможешь смоделировать эту проблему
- в любом случае - сообщение об ошибке должно быть полезным
- так что - советую проверить в этом режиме
- */
- By locatorToString;
- /*
- Имя locator - более точное в данном случае
- */
- ******************************************
- /*
- хорошая идея - реализовать умное ожидание в отдельном классе
- с точки зрения Single Responsibility - так будет лучше
- наглядно этот класс назвать WaitFor
- а статический метод-умный ожидатель выполнения кондишена - until
- и в коде мы будем писать waitFor(byCss(...)).until(visible())
- получится очень наглядная и понятная фраза
- чтобы так писать - waitFor(byCss(...)).until(visible())
- реализуй конструктор с параметром By locator
- реализуй статический метод waitFor с параметром By locator
- возвращающий - новый объект класса WaitFor
- реализуй метод until(...)
- получишь
- waitFor(byCss(...)). - создали объект типа WaitFor
- until(visible()) - вызвали у него метод until
- конечно, until-у передаай как кондишен, так и таймаут
- уже второй вариант until-а - без таймаута (также как и раньше - такой берет таймаут по умолчанию
- сразу стоит реализовать вариант
- until(Condition<V>... conditions)
- как более универсальный
- как это работает - поочередно выполняем ждущую проверку для всех переданных кондишенов
- до тех пор пока какая-то проверка не упадет
- что возвращаем - результат последней ждущей проверки
- сама реализация ждущей проверки (с помощью FluentWait) - это ок
- просто - реализуй специальный класс для ждущей проверки
- тут цель - Single Responsibility Principle
- */
- ************************************************
- /*
- Сейчас кондишены реализованы как объекты анонимных классов
- Мы сможем больше пользы получить - если будем реализовывать каждый кондишен как класс имплементирующий интерфейс
- Condition<WebElement>
- или
- Condition<List<WebElement>>
- Иерархия для кондишенов все еще будет достаточно простая
- интерфейс
- и классы-кондишены, имплементирующие его
- Минимально необходимая реализация есть
- Будем ее развивать
- Далее - реализуй абстрактный класс-предок AbstractCondition<T> ,
- который имплементирует интерфейс Condition<T>
- Статья про нейминг
- http://www.vertigrated.com/blog/2011/02/interface-and-class-naming-anti-patterns-java-naming-convention-tautologies/
- Теперь смотрим дальше
- Нам хочется, чтобы наш toString() для любого кондишена был хорошо структурирован
- Это значит, чтобы фразы были типа
- __имя кондишена__ +
- for ____element/elements___ found by: ___locator___
- expected ___expected result description_____
- actual ____actual result description_____
- Можно конечно в каждом из кондишенов просто строить такую фразу
- согласно правилу
- А можно - toString() реализовать в абстрактном классе, и внутри этого метода - вызывать абстрактные методы
- а их уже - реализуем в потомках. Таким образом - структура сообщения любого из кондишенов
- будет одинаковой
- где брать ___locator___ тут - мы чуть позже поговорим...
- пока пропусти этот момент
- а вот для
- ____element/elements___
- ___expected result description_____
- ____actual result description____
- объяви и используй абстрактные методы
- identity()
- expected()
- actual()
- __имя кондишена__
- раз мы будем реализовывать кондишены как отдельные классы - потомки нашего абстрактного класса
- то откуда взять имя кондишена - у нас есть
- у нас есть имя класса кондишена - его можно использовать
- Далее
- element/elements
- Это только верхушечка айсберга
- Да, схема проверки кондишенов - у нас одна и та же
- Хоть это кондишен для списка элементов
- Хоть для элемента
- И нам нужно, чтоб наш waitUntil был один и работал с любым кондишеном
- Но
- Нам важна разница - что это за кондишен - для элемента или коллекции
- В данный момент - чтобы сказать про то в фразе - for ____element/elements
- А далее (в следующих заданиях) - чтобы кондишены для element - мы просто НЕ МОГЛИ применить к elements
- Таким образом, приходим к тому, что у нашего общего предка AbstractCondition<V>
- Будут 2 абстрактных потомка
- CollectionCondition extends AbstractCondition<List<WebElement>>
- ElementCondition extends AbstractCondition<WebElement>
- От которых мы будем наследовать наши реальные кондишены
- Потом это мы будем менять, но пока - это будет полезно
- И тогда, стоит держать 2 класса для реализации статических методов - вызовов кондишенов
- CollectionConditions и ElementConditions
- это будут классы-контейнеры статических методов
- типа
- public static Condition<WebElement> visible() {
- return new Visible();
- }
- чтобы уже в коде - не писать - new Visible()
- С иерархией разобрались немного )
- Возвращаемся к фразе в toString()
- Если этот метод реализовать в главном предке AbstractCondition,
- и этот метод будет использовать абстрактные методы, возвращающие строки
- для всех элементов этой фразы
- То структура сообщения о кондишене - будет всегда четкой
- А нам останется - реализовать эти абстрактные методы, возвращающие строки
- в потомках
- Тут смотри внимательно
- Некоторые из методов надо будет реализовать уже в кондишенах-потомках
- Некоторые - в CollectionCondition и ElementCondition
- Руководствуйся такими рассуждениями
- Если во всех потомках я пишу одинаковый код
- То лучше этот код написать в предке
- Что касается типа параметра condition для
- until(Condition<V> condition)
- Ты можешь указать интерфейс Condition<V>
- И метод будет корректно работать для любого из кондишенов
- Т к этот интерфейс лежит в основе этой иерархии
- */
- /*
- еще один наворот внутри кондишенов)
- мы его впоследствии переделаем
- сейчас нам это нужно для лучшего понимания процесса
- в apply передают локатор
- описание которого мы возвращаем в одном из методов кондишена (для ToString)
- и это - справедливо для всех кондишенов
- и нам - еще в AbstractCondition интересно сохранить этот локатор
- потому в AbstractCondition заводим переменную locator
- и реализуем метод apply
- в котором
- сохраняем переданный в apply локатор
- и вызываем абстрактный метод check
- (- который, собственно, и нужно будет в кондишенах реализовывать вместо apply)
- check - будет делать то же, что и apply делал ранее
- нам это нужно было для того, чтоб локатор получить еще на уровне абстрактного класса
- теперь - и в toString - мы можем подправить фразу - про локатор
- сразу подумай, какой модификатор доступа использовать для такой переменной
- http://www.quizful.net/interview/java/access-modifiers
- но это опять была только верхушка айсберга)
- посмотри на реализованные кондишены
- первой строкой в методе проверок - у тебя идет
- List<WebElement> elements = getDriver().findElements(elementsLocator);
- или
- WebElement element = getDriver().findElement(elementsLocator);
- (таких, кстати, пока нету
- реализуй
- visible
- present
- text
- exactText
- )
- Так вот
- фактически - вот это
- List<WebElement> elements = getDriver().findElements(elementsLocator);
- или
- WebElement element = getDriver().findElement(elementsLocator);
- это сущность типа V с точки зрения класса AbstractCondition
- давай метод check изменим
- пусть принимает не локатор
- а уже V entity (т е внутри кондишена в реализации check
- нам ничего не придется искать по локатору - нам это уже на вход передадут)
- а вот в реализации apply в AbstractCondition
- для получения этого V entity
- мы объявим новый абстрактный метод getWrappedEntity()
- который будет нам возвращать значение типа V
- а реализуем его в классах CollectionCondition и ElementCondition
- думаю, ты догадаешься, как )
- а вот потом, когда у нас будут наши классы для элементов (следующее задание)
- мы будем в apply передавать не локатор
- а сам элемент нашего нового класса
- и сам элемент будет возвращать getWrappedEntity()
- и мы тут, в кондишенах, избавимся от getWrappedEntity() и его реализаций )
- пока это такой костыль
- нарушающий принцип Single Responsibility
- но позволяющий нам сейчас максимально реализовать код
- который впоследствии изменим минимально
- */
- /*
- ну и еще один наворот на тему интерфейсов )
- как выше писала - класс может имплементировать несколько интерфейсов
- и прикольно - всю функциональность описывать на уровне интерфейсов
- это дает ряд интересных полезных эффектов
- вот тут например -
- описать интерфейс DescribesResult
- с методами expected() и actual()
- и пусть класс AbstractCondition<V> имплементирует как Condition<V>, так и DescribesResult
- первая мелочь - можно описания абстрактных методов отсюда убрать
- (они же и так объявлены в интерфейсе, а реализовывать мы будем в кондишенах уже)
- вторая приятная и важная вещь - соблюли лучше Single Responsibility Principle
- третья приятная вещь
- конечно, при условии, если таки для переменных-кондишенов будем использовать типы интерфейсов
- Condition<...>
- когда мы будем оперировать переменной
- Condition<...> condition
- то для такой переменной - мы не сможем вызвать методы expected() / actual()
- т е - там, где это было нужно - у нас эта функциональность была видна
- а там, где это не нужно - этого вообще не видно
- */
- ********************************************
- public static Condition<WebElement> visible() {
- return new Visible();
- }
- /*
- по идее - можно было еще короче)
- */
- public static Condition<WebElement> visible = new Visible();
- /*
- т е - реализовывать не метод, а статическую переменную
- вроде бы - лаконичнее, что хорошо
- но - не так уж и хорошо)
- Тут есть такая тонкость)
- мы создали статические переменные = объект-кондишен
- и используем их в проекте, который поддерживает параллелизацию
- если мы в параллельных тестах будем использовать ОДИН объект-кондишен ОДНОВРЕМЕННО
- могут возникать странные ошибки
- чтобы избежать этого
- вместо переменных стоит использовать методы
- тогда для каждого вызова кондишена мы будем использовать свой объект
- и таких проблем не будет
- наш текущий вариант с методом - грамотнее)
- */
Advertisement
Add Comment
Please, Sign In to add comment