Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- https://github.com/AleksanderPopov/automician_course/blob/master/questions_and_review/3_code_questions.txt
- ===============================================================================
- 1. "private By tasks = By.cssSelector("#todo-list li");
- //можно проще
- private ElementsCollection tasks = $$("#todo-list li");
- //а еще лучше - вот так
- private ElementsCollection tasks = $$("#todo-list>li");"
- >>>>>>>> так лучше локаторы хранить, или коллекции? в статье Солнцев
- писал что если бы ему нужно было бы переиспользовать элемент,
- он бы сохранил локатор как поле...
- /*
- мы рекомендуем хранить коллекцию
- для селениде - это ничем не хуже
- по сути переменная - ElementsCollection tasks - ничем не хуже локатора
- объявляя и инициализируя ее - мы просто описываем - что и как мы будем искать
- когда это понадобится
- т е по сути - эта часть функциональности - очень похожа на локаторы
- т е - ничем не хуже
- а еще - когда начнем работать с ElementsCollection tasks - будем писать меньше кода
- что тоже приятный бонус)
- так что - оперируй переменными типов ElementsCollection и SelenideElement - без проблем
- по той статье - там ты видел - какой код Солнцев комментировал)
- http://automation-remarks.com/2016/selenide-shadow-sides/index.html
- если будешь отдельно - открывать урл
- и отдельно - инициализировать пейдж
- то проблем с использованием полей типа ElementsCollection и SelenideElement - точно не будет
- Если нравится вариант - когда вынесены в переменные именно локаторы
- а уже в самих методах - мы получаем по этим локаторам - элемент или коллекцию
- то это в общем тоже ОК
- Особенно с учетом - что так сам автор Selenide говорит)
- Я вижу в таком подходе такие плюсы
- решение очень похоже на чистое селениумское решение (грамотное)
- там тоже - лучший вариант - не через @FindBy прокси элементы/списки работать
- и именно - испольуя локаторы
- но то мы попозже поговорим про Selenium
- и второй момент
- по имени переменной tasks - типа - еще догадаться нужно - что оно такое
- в отличие от $$(tasks)
- Однако - я остаюсь при своем мнении)
- я в своем коде - использую именно переменные типа ElementsCollection и SelenideElement
- (ну или в методах - зашиваю работу с $$(....) $(....) - если селектор лишь для реализации этого метода нужен
- то никаких переменных не нужно вообще)
- причина проста - мы и так на уровне тестовой логики оперируем неким набором формальных понятий
- и от того, что у нас к тому же будет более формализована работа с переменными - я не вижу проблемы
- (tasks или $$(tasks) - я выберу tasks - т к в переменные мы выносим то, что используется не единожды
- это не представляет труда запомнить - что за сущность. А писать - чуть меньше))
- Яков тоже так поступает, как я описала)
- Технически - отлично все работает - когда ты оперируешь переменными типа ElementsCollection и SelenideElement
- когда инициализаия пейджа не сопряжена с открытием урла
- я так и делаю - открываю урл отдельно, пейджи инициализирую отдельно
- и все ок)
- Если тебе покажется лучшей идея - оперировать локаторами
- ну... наверное я не буду спорить)
- но я так точно не стала бы делать)
- */
- ===============================================================================
- 2. "что касается селектора "#todo-list>li"
- то он - точнее
- для нашего примитивного прилодения - и твой вариант ок
- но иногда эта разница бывает существенной"
- >>>>>>>> да, я знаю в чем разница) конкретно в этом случае не нужно
- было искать элемент непосредственно на уровень ниже я и не
- стал.
- /*
- да, я писала про это
- что и твой вариант ок
- в данном случае)
- */
- ===============================================================================
- 3. "вариант обхода - for (SelenideElement e : $$(tasks))
- плох тем, что на момент старта обхода - на странице могут еще недогрузиться элементы в списке
- получается - ненадежно и код раздутый"
- >>>>>>>> в смысле недогрузится? я думал когда мы обращаемся к
- $$(tasks) он сначало находит все, что может, а потом уже
- отдает нам коллекцию, разве не так?
- ***********************************
- for (SelenideElement e : $$(tasks))
- ***********************************
- и
- ***********************************
- ElementsCollection c = $$(tasks);
- for (SelenideElement e : c)
- ***********************************
- не одно и то же чтоли? типа в первой ситуации это ненадежно,
- а во второй надежно?
- /*
- оба варианта, которые ты привел - одинаково ненадежны)
- $$(tasks) - лишь рассказ про то - как искать коллекцию
- $$(tasks) - мы начинаем ее обход, и получим коллекцию, которая есть на этот момент
- ты знаешь универсальный способ определить - что коллекция догрузилась?
- любая коллекция, сферическая в вакууме )
- а как мы работаем обычно - как юзеры
- мы что - ждем сидим этого - что что-то догрузилось
- обычно - как только мы видим нужные нам элементы - мы начинаем с ними взаимодействовать
- не более того, никаких ожиданий - когда загрузится все и вся - мы не делаем
- чтоб с чем-то взаимодействовать - нам достаточно - чтоб это у нас было
- использовать обход коллекции, чтоб проверить все ее элементы - это неудачный подход
- используй should - проверки для коллекции
- можешь полюбопытствовать - как это устроено
- в should- проверке в рамках таймаута - выполняются проверки коллекци по кондишену
- что фактически происходит
- например, мы задали проверку collection.shouldJave(size(....))
- в рамках таймаута
- получаем список вебэлементов - по информации о collection (ты помнишь - по сути - это рассказ о том, как получать список элементов)
- если его размер равен ожидаемому - все, проверка прошла
- все ок
- если нет - ожидаем миллисекунды (нет смысла без задержки новую проверку делать - только ресурсы жрать)
- и снова проверяем
- если в таком цикле в рамках таймаута - проверка не прошла - наш тест падает
- если прошла - проходит
- мы, вызывая проверку collection.shouldJave(size(....)) - сами задаем -
- чего нам нужно дождаться/что ам нужно проверить
- в то же время, у коллекции есть метод size()
- он вернет - тот размер, которій у коллекции есть на момент вызова этого метода
- итого - проверки для коллекции - делай не через обход ее элементов в цикле
- а через работу с should - проверками для коллекции
- автор теста лучше всех знает - чего ему нужно дождаться
- а если говорить о доступе к конкретному элементу коллекции
- то тоже - не нужно его искать в цикле
- получи нужный элемент этой коллекции
- через уточнение
- SelenideElement element = collection.get(...)
- SelenideElement element = collection.findBy(...)
- это по сути - рассказ - как получить элемент (в этот момент мы еще ничего не получаем)
- а вот когда начнем взаимодействовать с элементом (кликать, значение вводить и прочее)-
- то сначала будет выполнено ожидание его видимости (аналогичная ждущая проверка)
- и потом - работа с ним
- т е - очень похоже на то, что делает человек, работая с сайтом
- увидел нужное ему - и давай юзать)
- такие обходы - for (SelenideElement e : $$(tasks)) - теоретически
- я бы делала после того - как выше по коду дождалась нужного мне
- но - пока прецедентов не было)
- реально - имея выше описанные способы работы - это пригодится крайне редко
- если вообще пригодится)
- код - когда ты юзаешь проверки, или когда ты доступаешься к элементам коллекции через методы этой коллекции - проще, чище, надежней
- все сложности и ухищрения - все под капотом
- тебе остается думать о тестовой логике
- что очень приятно - ведь решать одну задачу вместо нескольких - проще
- */
- ===============================================================================
- 4. "что до реализации метода
- его конечно реализовывать - не нужно было
- но уже если делать - то чтоб он был надежнее и проще - лучше сделать вот так
- for (String taskName : taskNames) {
- destroy(taskName);
- }
- и все)"
- >>>>>>>> я специально отдельно реализовал, для того чтоб если вдруг
- нам понадобится удалить 100 тасок, мы не 100 раз переискивали
- весь список, а один.
- /*
- нам такого не понадобится)
- по крайней мере - пока мы не решим реализовывать нагрузочное тестирование таким способом)
- а это довольно экзотичный способ будет (хотя наверное - и такой имеет право на существование)
- и там в проекте еще понадобятся дополнительные инструменты - нам же еще и намерять нужно - что мы нагрузили
- то - отдельная тема
- и в курсе мы этого не касаемся - нагрузочного тестирования
- для функционального тестирования - мотивы я описала в ревью ранее - почему нам не нужен такой метод
- */
- ===============================================================================
- 5. "чтобы избежать этого
- вместо переменных стоит использовать методы
- тогда для каждого вызова кондишена мы будем использовать свой объект
- и таких проблем не будет"
- >>>>>>>> это как? я чет не понимаю о чем речь, в том же селениде
- все описано либо так
- *************************************
- public static final Condition hidden = new Condition("hidden", true) {
- @Override
- public boolean apply(WebElement element) {
- try {
- return !element.isDisplayed();
- } catch (StaleElementReferenceException elementHasDisappeared) {
- return true;
- }
- }
- };
- *************************************
- либо так
- *************************************
- public static Condition matchText(final String regex) {
- return new Condition("match text") {
- @Override
- public boolean apply(WebElement element) {
- return Html.text.matches(element.getText(), regex);
- }
- @Override
- public String toString() {
- return name + " '" + regex + '\'';
- }
- };
- }
- *************************************
- у меня описано инициализация переменной как в случае 1
- т.к. мне не нужно параметры передавать в ajaxCompleted.
- Добавил final На всякий случай, хотя эту переменную всу
- равно должны только читать, используя свой инстанс драйвера.
- Или ты имеешь ввиду вместо переменной ajaxCompleted сделать
- метод, который её возвращает, т.к. вызывать мы его будем
- только в хелпере, в методе waitForAjaxCompleted()?
- /*
- Да, ты прав, в Selenide - именно так и есть - такого рода кондишены - объявлены как статические переменные
- и в общем - это может быть источником проблем при паралльной работе тестов - если кондишен оперирует какими-то
- переменными-состояниями (как например - у тебя - переменная result)
- public static final ExpectedCondition<Boolean> ajaxCompleted = new ExpectedCondition<Boolean>() {
- boolean result;
- @Override
- public Boolean apply(WebDriver driver) {
- try {
- result = (Boolean) ((JavascriptExecutor) driver).executeScript("return typeof($) !== 'undefined' && $.active == 0");
- System.out.println("Ajax return $.active == 0 is: '" + result + "'");
- } catch (Exception e) {
- return false;
- }
- return result;
- }
- public String toString() {
- return "Ajax return $.active == 0 is: '" + result + "'";
- }
- };
- ajaxCompleted = один объект
- и если его одновременно будут в разных потоках - дергать
- то это поле - result - будет отражать состояние - то для одного потока, то для другого
- конечно - на такие грабли - наступить тяжеловато, но возможно
- а если объявить метод
- public static final ExpectedCondition<Boolean> ajaxCompleted() {
- return new ExpectedCondition<Boolean>() {
- ...
- то - для каждого вызова проверки такого кондишена - будет свой отдельный объект-кондишен
- и таких проблем гарантированно не будет
- реализуй кондигены методами - даже если у них нет параметров - ajaxCompleted()
- этот вариант правильнее
- */
- ===============================================================================
- 6. "public TaskPage completedShouldHaveTexts(String... texts) {
- public TaskPage completedListShouldBeEmpty() {
- /*
- Методы реализованы пристойно
- но - они все равно не нужны)
- про это Яков говорил в видео
- https://drive.google.com/file/d/0B8hgIBw8-V-AUDhxWDg1YmYxM3c/view?usp=sharing
- примерно с 58-ой минуты, минут 5-7 посмотри"
- >>>>>>>> типа мы сейчас пишем функциональные тесты, поэтому нет
- смысла проверять что к задачке добавился класс, а лучше
- проверить функционал toggle -> filter, clearCompleted ?
- /*
- мы и сейчас, и далее везде на курсе - пишем функциональные тесты)
- и да, верно, закомпличивание можно проверить тобой описанными способами
- этого касалась и в ревью на твой сценарий
- думаю, уже вопросы должны были отпасть
- */
- ===============================================================================
- 7. "а вот с destroy("2", "3") - другая история уже
- тут пропуск проверки повлияет на точность проверок"
- >>>>>>>> почему повлияет? если мы пытаемся удалить например 2,
- а удаляется 3-я, мы поймем т.к. не найдем элемент. Если просто
- ничего не удаляется, или удаляется не то, то тоже поймем, т.к.
- после удаления проверяем что осталось. Где же мы тогда потеряли
- по точности?
- /*
- тут вариантов - много на самом деле - как и что может пойти не так
- общее правило такое - каждая операция должна быть проверена сразу
- а покрывать дважды подряд удаление - я даже для full coverage - не вижу смысла
- разумнее в разных тестовых ситуациях и на разных контекстах удалить по одной таске и проверить эти результаты
- ну то уже будем делать - когда full coverage будем реализовывать
- в общем - бесполезная функиональность - метод delete с несколькими параметрами
- в данном случае)
- конечно, в жизни будет куча задач - когда действительно - понадобятся методы
- заполняющие сразу кучу полей, или включающие кучу переключателей
- т к у нас - на эти поля и секбоксы = может быть какая-то одна неразделимая логика =
- заполнили + наклацали и только потом поверили
- но в даном случае = удаление таски = отдельно проверяемое действие, со своей отдельной логикой
- */
- ===============================================================================
- 8. "не понимаю ценности BaseTest поясни, что из реализованного в BaseTest - ценно"
- >>>>>>>> ну как минимум он нужен для 'Configuration.browser = "firefox";'
- /*
- по умолчанию - так оно и есть)
- потому - ничего и не нужно делать
- */
- ===============================================================================
Advertisement
Add Comment
Please, Sign In to add comment