Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- actualState = (element != null ? true : false);
- //или
- actualState = (element != null);
- /*
- это - одно и то же технически
- рекомендую второй вариант)
- */
- ****************************
- new Actions(getDriver())
- /*
- вот этот код - можно завернуть в метод actions() - в ConciseAPI
- */
- ******************************
- @Override
- public LazyElement setValue(CharSequence... charSequences) {
- System.out.println(this.toString());
- clear();
- sendKeys(charSequences);
- return this;
- }
- /*
- отладочное сообщение - убирай
- и в clear(), и в sendKeys - уже встроены ожидания
- получается - тут мы дважды ждем
- чуть оптимальнее было бы - подождать видимости - один раз
- а затем для вебэлемента вызвать и clear(), и sendKeys(..)
- так будет менее DRY, конечно
- нас оправдывает именно то, что мы оптимизируем действия
- */
- *****************************************
- if (apply != null) {
- return true;
- }
- /*
- можно проще в is(..) поступить
- return (apply != null);
- */
- *****************************************
- public class LazyElementInnerElement extends AbstractLazyElement {
- ...
- private LazyEntity<WebElement> parentElement;
- /*
- почему не LazyElement?
- достаточно уточниться до интерфейса, который имплементируется родительским объектом
- глубже по иерархии не нужно идти
- это же касается и родительских сущностей в других классах
- LazyCollectionNthElement, например
- если мы точно знаем - что есть родительская лейзи-сущность - элемент или коллекция
- так отлично - используй интерфейсы LazyElement или LazyCollection
- а вот если не знаем - что есть родительская лейзи-сущность - то уже применяем LazyEntity(и не уточняем generic тип)
- */
- ....
- @Override
- public String toString() {
- return "LazyElementInnerElement{" +
- "locator=" + locator +
- " from " + parentElement +
- '}';
- }
- /*
- советую применять в toString() - простые лаконичные и точные формулировки
- и избегать применять наши термины - имена классов
- просто опиши - как мы получили этот элемент
- этого достаточно
- return parentElement + " find(" + innerLocator + ")";
- упрости и в других лейзи-сущностях выражения в toString()
- там, где отталкиваемся только от локатора - достаточно просто локатора
- */
- ***********************************************
- public class LazyWrappedWebElement extends AbstractLazyElement {
- ....
- @Override
- public String toString() {
- return "LazyWrappedWebElement element = " + element + " from "+ parentEntity + "}";
- }
- /*
- return "WebElement " + element + " from " + parentEntity; ?
- */
- *************************************************
- public class LazyCollectionFilterCollection extends AbstractLazyCollection{
- /*
- лучше не Filter, а Filtered
- в данном случае - можно подсократить до LazyFilteredCollection
- причины - и так ясно - что за источник
- раз получили на выходе - коллекцию, отфильтровав что-то
- но это что-то - тоже коллекция
- не настаиваю на сокращении
- */
- ...
- @Override
- public List<WebElement> getWrappedEntity() {
- /*
- почти идеальная реализация
- почти - потому что в List<WebElement> filteredElements
- правильнее добавлять не LazyWrappedWebElement, а сам webElement
- еще можно было бы сэкономить на создании LazyWrappedWebElement
- если бы метод check кондишена біл нам доступен
- он уже принимает WebElement и возвращает нам нужній да/нет
- можешь для начала просто вынести метод check на уровень интерфейса Condition
- потом это лучше структурируем
- */
- @Override
- public String toString() {
- return "LazyCollectionFilterCollection{" +
- "parentCollection = " + parentCollection.getClass().getSimpleName() +
- ", condition = " + condition +
- '}';
- }
- /*
- Для кондишена - применяй тоже getClass().getSimpleName()
- нам достаточно этих подробностей этих (вспомни, как toSrting кондишена формируется - так нам точно не надо)
- return parentCollection.toString() + " filter(" + condition.getClass().getSimpleName() + ")"; - будет ок
- не настаиваю именно на моих вариантах
- смысл - чтоб даже тот, кто не полез разбираться в наш фреймворк
- понимал тексты об ошибках
- и по описанию элемента - узнавал свой код
- */
- ***************************************
- public class LazyCollectionFindElement extends AbstractLazyElement {
- /*
- LazyCollectionFoundElement
- LazyCollectionFoundByConditionElement - мне вот такой вариант больше нравится
- */
- @Override
- public WebElement getWrappedEntity() {
- /*
- тут - та же история, как и для LazyCollectionFilterCollection
- */
- @Override
- public String toString() {
- /*
- Тут - похоже тоже на вариант для LazyCollectionFilterCollection
- */
- *****************************************
- public interface Condition<T> {
- T apply(LazyEntity<T> entity);
- boolean check(T wrappedEntity);
- }
- /*
- делаем красиво)
- сейчас в этом интерфейсе - объявлены методы
- служащие как для принятия решения - прошли-не прошли
- так и для получения результата ожидания
- нарушаем Single Responsibility Principle )
- реализуй интерфейс Matcher <T> с объявленным методом boolean check(T wrappedEntity);
- а в Condition <T> - останется apply
- Condition <T> - отнаследуй от Matcher <T>
- используй check и для метода is
- */
- **************************************
- public void delete(String taskText) {
- tasks.find(text(taskText)).hover();
- $(".destroy").click();
- }
- /*
- вот это слишком большое упрощение - $(".destroy")
- нам откуда нужна эта кнопка
- четко из таски, на которой делали hover()
- потому - оттолкнись от правильного элемента
- и внутри него ищи кнопку
- это и других методов этого пейджа касается
- тут тебе поможет код, который использовала в Selenide
- */
- *************************************************
- реализовала новые методы. Тесты падают на doubleClick и hover.
- /*
- суть в чем
- например, при первом получении getWrappedEntity для
- tasks.find(cssClass("editing")).find(".edit")
- в getWrappedEntity для LazyElementInnerElement
- мы выполняем
- parentElement.getWrappedEntity().findElement(innerLocator)
- а в этот момент
- parentElement.getWrappedEntity() = null
- и мы получается у null пытаемся получить findElement(innerLocator)
- и тут происходит ошибка, которую мы не ловим
- и которую ловить - нельзя (технически - можно, но - не правильно)
- вот линка, почитай, достаточно внятная
- http://howtodoinjava.com/core-java/exception-handling/how-to-effectively-handle-nullpointerexception-in-java/
- а потом углуби понимание http://www.yegor256.com/2014/05/13/why-null-is-bad.html
- особенно внимательно на вот этот кусок посмотри http://joxi.ru/5md7jYwtvV5k8r
- получается - что good practice = обходить эти проблемы
- а не перехватывать NullPointerException
- можно конечно поступить так (это первое, что приходит в голову, НО МЫ ТАК ДЕЛАТЬ НЕ БУДЕМ)
- переписывать getWrappedEntity()
- вместо
- return parentElement.getWrappedEntity().findElement(innerLocator);
- пишем
- return parentElement.getWrappedEntity() == null ? null : parentElement.getWrappedEntity().findElement(innerLocator);
- аналогично - и в других классах
- кроме этого - поправить apply кондишена - чтобы check вызывался только если его аргумент не равен нуллу
- как ты понимаешь - это куча изменений, и достаточно легко что-то пропустить
- лучше - опереться на одно правило и его придерживаться в коде
- правило - наш код не возвращает null,
- вместо этого - вызываем исключение (напоминаю про http://joxi.ru/5md7jYwtvV5k8r)
- Дорабатываем наши кондишены
- метод check - у нас уже возвращает значение типа boolean (прошла/не прошла проверка)
- в apply -
- если проверка не прошла - вместо того, чтобы возвращать null -
- вызываем исключение WebDriverAssertionException (отнаследуй от WebDriverException)
- тут, в apply не применяем try-catch
- В WaitFor
- дорабатываем метод until
- как раз тут у нас уже есть try-catch
- получается, что если apply бросит ошибку
- то мы попадем в секцию catch ( в которой эту ошибку просто запомним - в переменную)
- (WebDriverException - мы тут ловим, значит - поймаем и WebDriverAssertionException)
- а если apply не бросит ошибку - значит все ок - проверка прошла
- потому и делаем сразу return condition.apply(lazyEntity)
- итого получили
- в реализация кондишена - мы нуллы нигде не возвращаем (или хорошее значение, или исключение)
- в реализации вейт антила - тоже самое
- причем исключения - используем разные (каждая ситуация = свое исключение)
- следующий момент
- еще один источник null - результат getWrappedEntity() для потомков AbstractLazyElement
- для коллекций такого не будет - с будет просто пустой список
- а вот с элементами - да, будет
- потому - надо чуть доработать код в getWrappedEntity() для классов семейства LazyElement
- применяем ту же логику - мы нуллы нигде не возвращаем (или хорошее значение, или исключение)
- в AbstractLazyElement реализуем getWrappedEntity()
- который вызывает абстрактный метод fetchWrappedEntity() (новый, возвращающий WebElement)
- и если результат fetchWrappedEntity() равен нулл
- то getWrappedEntity() вызовет исключение
- ElementNotFoundException (также отнаследуем от WebDriverException,
- раз у нас уже несколько своих исключений - в core создай для них свой пекедж exceptions)
- а реализация fetchWrappedEntity() - это то, что мы ранее реализовывали в getWrappedEntity()
- и тут тоже, смотри внимательно
- если мы сами пишем код (а не вызываем что-то стороннее)
- то тоже также - мы нуллы нигде не возвращаем (или хорошее значение, или исключение)
- получили и для getWrappedEntity() - нулла нам теперь этот метод не возвращает
- а это значит - не надо думать про вызовы getWrappedEntity().... - как
- про источник NullPointerExteption
- еще небольшой наворот, раз уж мы так развили работу с исключениями
- в getWrappedEntity() класса LazyCollectionNthElement
- перехватим с помощью try-catch исключение IndexOutOfBoundsException
- и вместо него бросим свое исключение
- LazyСollectionIndexOutOfBoundsException (также отнаследуем от WebDriverException)
- что это нам даст
- в WaitFor.until
- можно ловить только WebDriverException
- т е - получили более стройную логику
- а за счет того, что помимо ранее выводимой детальной информации
- о проверке, мы еще и выводим информацию об ошибке, которая привела к тому,
- что ошибка не прошла - информация о проблеме - максимально полная
- да и то, что мы нигде сами не возвращаем нуллы
- сделало наш код надежнее и проще
- теперь - тесты todoMVC падать не должны)
- */
- **********************************************
- /*
- еще немного challenge )
- в интерфейсе LazyElement
- объяви методы
- LazyCollection findAll(By innerLocator);
- LazyCollection findAll(String innerCssSelector);
- и реализуй их в AbstractLazyElement
- этот метод позволит у элемента - по селектору или локатору - получить коллекцию внутренних элементов
- отладиться можно - используя
- LazyCollection tasks = $("#todo-list").findAll("li");
- или
- LazyCollectionLazyCollection emails = $("[role='main']").findAll(".zA");
- Иногда такой метод findAll будет очень полезным
- принципы - те же
- как ты до этого реализовывала методы get, find, filter ...
- так сказать - для закрепления результатов)
- */
- **************************************
- /*
- насчет использования модификатора доступа protected
- есть такая точка зрения, что использовать protected - это самообман
- т к в рамках потомков можно для отнаследованного protected-метода
- этот метод сделать публичным
- когда мы использовали protected, мы хотели, чтобы что-то было с возможностью наследования
- и не хотели его внешнего использования.
- а кто-то отнаследовался от нашего класса и объявил это как public
- и вот то, что мы прятали внутри класса - уже "торчит" наружу
- есть good practice - избегать применения protected
- то, что мы что-то объявим внутри класса-кондишена как public
- не будет нам особо мешать при работе с нашим фреймворком
- т к в итоге - используем для кондишена тип интерфейс,
- и это позволит не светить нашими публичными переменными/методами
- когда будем юзать фреймворк
- идеально про протектед-методы
- http://programmers.stackexchange.com/questions/162643/why-is-clean-code-suggesting-avoiding-protected-variables
- http://joxi.ru/52akqzoUGnqYGr
- http://stackoverflow.com/questions/3631176/why-are-many-developers-opposed-to-using-the-protected-modifier-in-oop
- http://stackoverflow.com/questions/37011/should-you-ever-use-protected-member-variables
- http://stackoverflow.com/questions/4913025/reasons-to-use-private-instead-of-protected-for-fields-and-methods
- http://www.javalobby.org/java/forums/t77911.html
- Просмотри весь проект, перейди на использование public
- */
Advertisement
Add Comment
Please, Sign In to add comment