Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- вопросы: я не уверен насчет всех этих выводов ошибок.
- Не пойму, как оно должно ложиться )
- Вот например, элемент не найден - http://joxi.ru/EA4vwLyfDeYy4m вообще ниче не ясно в выводе.
- а вот ошибка "не совпадает текст" - http://joxi.ru/n2Y5EgPTjgaBN2 - тут все окее
- public class WaitFor<V> {
- ...
- public V until(Condition<V> condition, long timeout) {
- ...
- do {
- try {
- V result = condition.apply(smartEntity);
- ...
- } catch (IndexOutOfBoundsException | WebDriverException e) {
- lastError = e;
- }
- sleep(Configuration.pollingInterval);
- }
- while (System.currentTimeMillis() - startTime < timeout);
- throw new TimeoutException(String.format("Assert failed while waiting '%s' seconds " +
- "\n to assert: %s", timeout / 1000, condition.toString()), lastError);
- }
- /*
- не смогла получить ошибку как на http://joxi.ru/EA4vwLyfDeYy4m
- думаю, она проявлялась раньше - когда вот тут - мы не ловили такого эесепшена
- сейчас - ловим и его предка тоже - WebDriverException
- потому - и видим красивое Assert failed while waiting ...
- если у тебя и сейчас проявляется ошибка - маякни - обсудим
- */
- ***************************
- http://joxi.ru/5md7jYwtvD1Vvr
- /*
- для smart-сущностей - будет отдельный пекедж entities на уровне core
- и там тоже будет деление на element & collection
- в корне entities - будут интерфейсы и общие классы
- а в element & collection - уже элементы и коллекции
- */
- *************************************
- public class Text extends ElementCondition {
- private String expectedText;
- private String actualText;
- /*
- ты от этого кондишена наследуешься
- поменяй у полей объекта - модификаторы доступа - чтоб поля и у потомков были доступны
- гугли access modifiers java
- */
- public Text(String text) {
- this.expectedText = text;
- }
- /*
- точно такой конструктор - будет нужен и в потомке
- это просто пока запомни
- */
- public Text() {
- }
- /*
- а такой конструктор - вообще не нужен
- нам не надо уметь вызывать этот кондиен с пустым параметром
- */
- ...
- @Override
- public String expected() {
- return expectedText;
- }
- @Override
- public String actual() {
- return actualText;
- }
- /*
- точно такие же методы - будут нужны и в потомке
- это просто пока запомни
- */
- }
- //смотрим на потомка
- public class ExactText extends Text {
- private String expectedText;
- private String actualText;
- /*
- нам не понадобилось бы объявлять поля - тк поля, объявленные в прдке - были бы доступны
- */
- public ExactText(String text) {
- this.expectedText = text;
- }
- /*
- т к нам надо от конструктора ровно то же самое - просто используем super(text);
- http://developer.alexanderklimov.ru/android/java/extends.php
- http://metanit.com/java/tutorial/3.5.php
- https://docs.oracle.com/javase/tutorial/java/IandI/super.html
- */
- @Override
- public boolean check(WebElement element) {
- actualText = element.getText();
- return actualText.equals(expectedText);
- }
- /*
- check, конечноб и тут придется реализовать, это верно
- а вот на expected() и actual() - ты верно заметил - можно сэкономить
- только и полями объекта надо еще оперировать одними и теми же)
- вот это тоже - очень важно привести в порядок
- */
- }
- *******************************
- public SmartElement shoudBe(Condition<WebElement> condition) {
- waitFor(this).until(condition);
- return this;
- }
- /*
- не shoud, а should )
- и давай реализуем вариант поуниверсальнее - martElement shoudBe(Condition<WebElement>... conditions)
- тут - просто вызовем public V until(Condition<V>... conditions)
- суть такого метода - мы поочередно для переданных кондишенов вызываем until
- как только результат = нуллу = прекратили
- в конце - возвращаем результат последней проверки
- касается как элементов, так и коллекции
- */
- **************************
- public String toString()
- /*
- этот метод есть еще у общего потомка всех классов - Object )
- @Override - надо использовать
- технически - ты вообще можешь эту аннотацию не использовать
- но - согласно конвеншенсов она нужна
- https://google.github.io/styleguide/javaguide.html#s6.1-override-annotation
- http://stackoverflow.com/questions/212614/should-we-override-an-interfaces-method-implementation
- http://stackoverflow.com/questions/94361/when-do-you-use-javas-override-annotation-and-why
- по второй линке тоже похожие вопросы рассматриваются)
- и народ про них спорит (в варианте - когда мы имплементируем метод интерфейса - там тоже надо аннотировать)
- - есть и pro, и contra )
- но вот у гугла в конвеншенсах - указано использовать
- и у SmartCollection - аналогичный метод дореализуй
- */
- ******************дальше учитывай - Lazy = Smart*************************
- /*
- у нас - будет несколько классов - лейзи-элементов и лейзи-коллекций
- все лейзи-элементы - должны будут уметь делать - один и тот же перечень действий
- и аналогично - про лейзи-коллекции
- логично использовать для лейзи-элементов и лейзи-коллекций
- интерфейс (потомок LazyEntity)
- тут объявляем все методы-действия
- абстрактный класс
- тут реализуем все одинаковое
- потомки -
- тут реализуем только то, что отличает различные классы друг от друга
- конструктор + необходимые поля
- toString
- getWrappedEntity
- эти новые интерфейсы логично назвать LazyElement & LazyCollection
- абстрактные классы - AbstractLazyElement & AbstractLazyCollection
- и конечные классы - будут иметь более детальные имена
- (Lazy+откуда+уточнение+Element или Lazy+откуда+уточнение+Collection)
- при таком нейминге - мы учитываем конвеншенсы (про это они тоже есть)
- ниже дам линки
- с классами-лейзи-элементами
- как у нас будут обстоять дела
- лейзи-элементы любого вида - должны уметь то - что описано в interface LazyElement
- но - получать вебелемент мы будем - по-разному (фактически - реазицация getWrappedEntity)
- например
- мы уже реализовали - getWebDriver().findElement(locator)
- а будет еще и такой вариант - получить вебэлемент - для такого лейзи-элемента
- lazyElement.find(".destroy")
- lazyCollection.find(cssClass("edited"))
- lazyCollection.get(3)
- т е - в итоге - у нас будет несколько классов, имплементирующих interface LazyElement
- причем - отличаться у них будут лишь конструкторы и реазицация getWrappedEntity (ну и toString)
- а реализация остальных методов, да и вообще набор методов - один-в-один
- вот и получим такую иерархию
- самый общий интерфейс
- LazyEntity<T>
- его потомки - интерфейсы
- LazyElement
- LazyCollection
- абстрактные классы
- AbstractLazyElement
- AbstractLazyCollection
- (реализуем все методы, кроме абстрактного getWrappedEntity,
- ну и конструктор нам тут не нужен - т к класс абстрактный)
- набор классов - для элементов и коллекций
- наши уже разработанные варианты
- LazyWebDriverElement
- LazyWebDriverCollection
- (WebDriver = откуда, см схему в нейминге - выше писала)
- список будет пополняться)
- получишь что-то такое http://joxi.ru/GrqLOX3SNZPLoA
- пока закрасила те классы - которые еще у нас не появились)
- ну и разумно - это в рамках core - выделить в отдельный пекедж
- wrappers или entities - хорошее общее название
- т к вот эти наши классы - это лишь обертка вокруг вебэлемента/списка вебэлементов
- есть конвеншенсы и по этому поводу - какие имена давать интерфейсу, какие - абстракному предку,
- какие - классам-потомкам
- статья про это (лучший вариант)
- http://www.vertigrated.com/blog/2011/02/interface-and-class-naming-anti-patterns-java-naming-convention-tautologies/
- еще полезные линки
- http://stackoverflow.com/questions/2814805/java-interfaces-implementation-naming-convention
- https://web.archive.org/web/20130331071928/http://isagoksu.com/2009/development/java/naming-the-java-implementation-classes
- http://programmers.stackexchange.com/questions/152759/should-i-add-an-abstract-prefix-to-my-abstract-classes
- Interfaces, abstract classes - good practice
- http://stackoverflow.com/questions/1932247/abstract-classes-and-interfaces-best-practices-in-java
- http://joxi.ru/E2pdR1lFB1pyM2
- */
- ********************
- *
- следующий наворот )
- WebElement - это тоже интерфейс
- Если мы отнаследуем наш интерфейс LazyElement от WebElement
- Это то же самое что и заявить - "наш LazyElement - полная замена WebElement - и везде где вы его использовали
- вы теперь можете использовать наш лейзи враппер
- который помимо всех фич вебэлемента еще и будет ждать себя..."
- конечно, можно было бы абстрактный класс реализовать имплементирующим два интерфейса
- Класс может быть реализован от нескольких интерфейсов
- но тогда, при использовании переменной типа LazyElement - мы не получим функциональности WebElement
- потому тут правильнее именно отнаследовать LazyElement от WebElement
- Это в java-ООП мире достаточно прикольная практика -
- всегда, когда ты хочешь сделать какие-то штуки внутри объекта публичными -
- выносить их сначала в отдельный интерфейс - и потом его имплементировать
- потому что просто сделать какие-то штуки публичными - это недостаточно "строго"
- если ты четко заявляешь - мой объект поддерживает такие-то интерфейсы, тогда
- все могут общаться с твоим объектом через четко прописанное API этого интерфейса
- тут надо внимательно к вопросу подойти)
- в рамках LazyElement - не реализовывай методов, одноименных с методами WebElement
- да, мы для некоторых методов потерям chainable возможности
- но зато не будет двусмысленностей
- в абстрактном классе AbstractLazyElement - реализуй все вебэлементовские методы
- думаю, проблем с этим не будет
- думай - в каких методах и чего надо ждать - ничего не ждать / ждать видимости / ждать наличия в DOM
- */
- ***************************************
- /*
- следующий наворот )
- наворачиваем Collection )
- наследуем этот интерфейс и от Iterable<LazyElement>
- цель - уметь писать такой код
- elements = $$(...);
- for(LazyElement element:elements) {
- ...
- }
- т е уметь обходить нашу коллекцию элементов в таком варианте цикла
- чистая теория
- http://tutorials.jenkov.com/java-generics/implementing-iterable.html
- нам она практически не пригодится )
- просто для понимания - разумно ознакомиться
- достаточно понять, что
- при имплементировании интерфейса Iterable<...>
- надо будет реализовать метод
- public Iterator<ProxyElement> iterator()
- Дальше мы схитрим)
- List<...> тоже наследник Iterable<...>
- если мы внутри метода iterator()
- создадим и заполним список list типа List<LazyElement> элементами из коллекции
- то тогда можно вернуть в качестве результата метода iterator() - list.iterator();
- загвоздка в том, что в коллекции getWrappedEntity() возвращает значение типа List<WebElement>
- а мы хотим получить List<LazyElement>
- Вот и пришло время создать еще одного потомка для AbstractLazyElement
- назовем его LazyWrappedWebElement (обернутый веб элемент)
- будем передавать ему в конструктор вебЭлемент
- как ты понимаешь, его реазизация getWrappedEntity() - будет возвращать тот же веб элемент
- И теперь опять возвращаемся к методу iterator()
- Если в новый список List<LazyElement>
- Ты будешь складывать новосозданные объекты типа LazyWrappedWebElement
- То это будет ОК (т к LazyWrappedWebElement - потомок абстрактного класса который имплементирует интерфейс LazyElement)
- Проверяем новую функциональность на коде типа
- for (LazyElement element:tasks) {
- System.out.println(element.getText());
- element.shouldHave(text("а"));
- }
- */
- **********************************************
- /*
- ну и еще один наворот на тему интерфейсов )
- как выше писала - класс может имплементировать несколько интерфейсов
- и прикольно - всю функциональность описывать на уровне интерфейсов
- это дает ряд интересных полезных эффектов
- вот тут например -
- описать интерфейс DescribesResult
- с методами expected() и actual()
- и пусть класс AbstractCondition<V> имплементирует как Condition<V>, так и DescribesResult
- первая мелочь - можно описания абстрактных методов отсюда убрать
- (они же и так объявлены в интерфейсе, а реализовывать мы будем в кондишенах уже)
- вторая приятная и важная вещь - соблюли лучше Single Responsibility Principle
- третья приятная вещь
- конечно, при условии, если таки для переменных-кондишенов будем использовать типы интерфейсов
- Condition<...>
- когда мы будем оперировать переменной
- Condition<...> condition
- то для такой переменной - мы не сможем вызвать методы expected() / actual()
- т е - там, где это было нужно - у нас эта функциональность была видна
- а там, где это не нужно - этого вообще не видно
- */
- *************************
- /*
- сначала дочитай пассаж, потом решишь - надо ли что-то менять)
- мы немного смухлевали в структуре для кондишенов)
- CollectionCondition & ElementCondition - это как раз абстрактные классы-потомки конечных кондишенов
- но - в именах этих классов - мы не указывали Abstract
- Хотя методы, возвращающие кондишены или принимающие кондишены как параметры -
- оперируют интерфейсами - что ок, но не очень просто и лаконично
- переделать классы - CollectionCondition & ElementCondition
- в интерфейсы-потомки Condition<List<WebElement>> и Condition<WebElement>
- а сами кондишены отнаследовать от абстрактного класса
- (уточняя при этом - <List<WebElement>> или <WebElement>)
- возможно - понадобится имплементировать кондишены от CollectionCondition & ElementCondition
- (проверь, я не уверена в этом)
- И тогда методы будут возвращать значение типа интерфейс
- CollectionCondition & ElementCondition
- и в качестве типов параметров у методов should - будут использованы интерфейсы CollectionCondition & ElementCondition
- что будет чуть красивее
- ну и получаем варианты
- 0 - остаться при текущей иерархии кондишенов, в принципе все ок и так
- 1 - улучшить текущую версию и начать использовать интерфейсы CollectionCondition & ElementCondition
- (мы фактически внутрь фреймворка прячем подробности, которые выпирали в варианте 0)
- 2 - оставить как сейчас + использовать типы абстрактных классов CollectionCondition & ElementCondition -
- как типы возвращаемых методами значений и типы параметров методов
- Да, тут - нарушаем конвеншенсы ) Зато в целом код не такой загадочный)
- все варианты - с недостатками
- можешь оставить как есть
- можешь - попробовать улучшить
- */
Advertisement
Add Comment
Please, Sign In to add comment