Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- Ну, что сказать )
- Уже много чего умеешь
- Но, как это ни странно звучит - пока что будем версию упрошать
- Конечно, не до примитивного состояния, которое собственно как решение и требовалось,
- но до более простого - точно
- погугли - premature optimisation is evil
- далее в комментариях - буду указывать не самый простой и самый простой вариант реализации
- если будут сложности с реализацией не самого простого варианта - реализуй вариант попроще
- и дальше по курсу - все будет) только чуть позже
- */
- *******************************
- src/main/java/com/selenide/core/TaskManager.java
- /*
- класс TaskManager - содержит в себе методы, которые являются инструментами =
- это вспомогательные методы-действия для нашего приложения
- этот шаблон - называют Page Objects
- про это в контексте Selenide - см
- http://ru.selenide.org/documentation/page-objects.html
- мы более подробно рассматриваем это на курсе - но не сразу, а уже выполнив несколько заданий
- можешь оставить реализованный Page
- с условием - что будут соблюдены все требования (опишу ниже)
- если это покажется сложным - тогда - перенеси эти вспомогательные методы в тест-класс
- (мера временная, до разбора собственно темы пейджей)
- что надо поправить - если оставить пейдж
- имя класса пейджа - должно заканчиваться на Page (согласно conventions)
- все верно, такой класс располагают в ветке проекта src/main/java
- но только - в пекедже pages, а не core
- core - для более универсальных вещей, применимых для тестов разных приложений
- что касается пекеджей com/selenide - тут я бы поспорила )
- тут надо отразить - кто есть мы и что за проект мы делаем
- если бы
- мы = myOrganisation.com
- проект = myProject
- то получили бы такую структуру пекеджей
- com/myorganisation/myproject
- про это можно почитать в faq
- https://docs.google.com/document/d/10qSwWTQ6pGfVZSwOes-1QSmdflMiGD2U_y53VHq2m20/edit#heading=h.o5zpntq2790p
- чтобы переименовать класс
- https://docs.google.com/document/d/10qSwWTQ6pGfVZSwOes-1QSmdflMiGD2U_y53VHq2m20/edit#heading=h.vwuqi54t6fyg
- */
- ********************************************************
- public void tasksCreator(int numOfTasks) {
- for (int i = 1; i <= numOfTasks; i++) {
- $(By.id("new-todo")).setValue("task" + i).pressEnter();
- }
- }
- /*
- про имя метода - посмотри
- https://docs.google.com/document/d/10qSwWTQ6pGfVZSwOes-1QSmdflMiGD2U_y53VHq2m20/edit#heading=h.o7vsh4r93b55
- что делаем - создаем/добавляем таски -
- addTasks
- createTasks
- параметр - int numOfTasks
- ну, не лучший вариант )
- а если мне понадобится тест, добавляющий таски "a", "b", "c"?
- не надо внутри вспомогательных методов жестко диктовать - с какими тестовыми данными мы работаем
- решение - какие тестовые данные мы используем - это решение, принимаемое в тест-методе
- сравни код
- addTasks(4);
- vs
- addTasks("task1", "task2", "task3", "task4");
- второй вариант - лучше
- т к мы видим - что за таски мы создаем
- нам для понимания этого - достаточно тест-метод прочитать
- и не нужно углубляться в реализацию самого вспомогательного метода
- так что - это важное требование
- во вспомогательых методах - оперируем только тем, что в метод передали как параметр
- никаких решений о тестовых данных внутри вспомогательных методов быть не может
- реализуй этот метод по-другому
- чтобы можно было его вызвать вот так -
- addTasks("task1", "task2", "task3", "task4");
- если сложно(буквально в следующей лекции будем разбирать - как) -
- то чтобы можно было его вызвать вот так
- addTask("task1");
- addTask("task2");
- addTask("task3");
- addTask("task4");
- в наиболее простом варианте - пока вообще не оперировать методами
- просто в коде тест-метода - $(...).setValue(...).pressEnter();
- только тут будь последователен
- если от идеи использования методов отказываешься - то полностью)
- перепиши By.id("new-todo")
- можно в метод $(...) - передавать строку - css селектор
- получится лаконичнее
- */
- ********************************************************
- public void markTaskAsCompleted(String taskName) {
- String labelXpath = "//label[contains(text()," + "'" + taskName + "'" + ")]/../input";
- $(By.xpath(labelXpath)).click();
- }
- /*
- про имя метода - можно лаконичнее - completeTask
- но и это еще не все)
- https://docs.google.com/document/d/10qSwWTQ6pGfVZSwOes-1QSmdflMiGD2U_y53VHq2m20/edit#bookmark=kix.x92tktmmfsz2
- попробуй применить метод - completeTask к уже закомпличеной таске
- получим - таска будет переоткрыта
- т е - при другом контексте вызова метода - название метода не отражает того, что мы делаем
- а это - плохо
- подправь имя метода так - чтобы оно отражало суть действия вне зависимости от контекста
- мы тут не комплитим, мы - переключаем
- имя параметра - ну, можно и так
- имя таски, текст таски - да, хорошие термины в данном случае
- по поводу использования xPath
- да, инструмент мощный
- да, в случае, когда нам нужно найти некий элемент по тексту - вроде бы альтернатив нет...
- но)
- во-первых
- давай определимся - что мы ищем
- мы в списке тасок ищем таску с таким-то именем
- а внутри нее - ищем внутренний элемент такой-то
- когда нам еще придется работать со списком тасок
- при удалении таски
- при проверках состояния списка
- т е - работать с сущностью - список тасок - мы будем в нескольких вспомогательных методах
- погугли про DRY принцип
- правильно - не повторять в коде - одинаковые независимые селекторы
- т к - если потребуется менять код - то придется в нескольких местах вносить такие правки
- потому - грамотнее объявить переменную ElementsCollection tasks = $$(....)
- ... - CSS selector списка тасок - http://joxi.ru/v29WjP9hG6RnEr
- и далее - ее использовать в методах
- в самом примитивном варианте реализации этого задания - можно было
- для работы со списком тасок использовать $$("..."),
- если нужно что-то изнутри списка тасок - отталкивайся в любом случае от селектора списка тасок и его уже уточняй
- опять же - в самом простом варианте (пока) - можно обратиться
- к элементу списка по его номеру
- и затем - уточниться до внутреннего элемента - переключателя
- это все можно сделать используя один css селектор - $(....)
- потом - разовьем
- пока - можно и так
- как доступиться до нужного нам элемента $(...) - уточнить css selector списка тасок
- список тасок
- такая-то по счету таска (поиск таски по тексту - можно отложить пока)
- такой-то элемент внутри таски
- далеко не идеально, но уже структурно - это позволит потом переделать код меньшими усилиями
- если пока с переменной - сложно
- то - хотя бы вот так поступай
- везде, где отталкиваешься в рассуждениях от списка тасок - используй один и тот же селектор
- чтобы - когда дело дейдет до оптимизации кода - это легко было поправить
- а если пока с пониманием - ок - то развиваем)
- вообще, если сравнивать варианты
- для каждого элемента - свой независимый селектор
- или
- если элемент - часть уже обозначенной структуры - доступаемся к нему через эту структуру
- то - второй вариант дает больше преимуществ
- хотя бы потому - что читая такой код - понятнее - с чем мы имеем дело
- к этой теме мы будем еще возвращаться
- применительно к нашему случаю
- твой вариант = независимый селектор
- или
- вариант tasks.findBy(....).find(...)
- то - второй вариант - лучше
- чуть позже - как написать вот такой вариант tasks.findBy(....).find(...) - разберем
- но ты - попробуй разобраться сам. У тебя может получиться )
- И еще важный и не сложный момент
- input - не самый наглядный селектор для искомого элемента - переключателя состояния таски
- подбери селектор понагляднее
- */
- ***************************************************************
- // this overloaded method marks all tasks as completed
- public void markTaskAsCompleted() {
- for (SelenideElement label : $$(By.className("toggle"))) {
- label.click();
- }
- }
- /*
- тут требовалось выполнить действие - complete all
- используя вот такой переключатель http://joxi.ru/gmvqJvkHxXeZer
- имя метода - можно попроще - completeAllTasks
- но
- посмотри как работает этот переключатель - кликни на нем, когда и так все таски закомпличены
- так что - метод - не комплитит таски
- а переключает их
- учти это - когда будешь уточнять имя методу
- в этом методе к переключателям ты обратился как $$(By.className("toggle"))
- а в прошлом методе - такие же переключатели идентифицировал как input
- так лучше не делать
- одно и то же - получай одинаково.
- уже писала - методы $$ & $ - могут принимать в качестве параметра не только значение типа By
- но и строку - css селектор
- код получится попроще
- */
- *****************************
- public void clearCompleted() {
- $(By.id("clear-completed")).click();
- }
- /*
- перепиши $(By.id("clear-completed")) - используй css селектор как параметр для метода $
- правда, не хочется в имени метода указывать Tasks?
- на самом деле - в этом простеньком приложении - все операции = операции над тасками
- потому - можно из имен вспомогательных методов действий поубирать Tasks/Task -
- add, delete, ...
- не станет хуже от этого - т к по-прежнему - все однозначно
- мы знаем - что работаем с тасками
- хоть еще ни одного метода с проверками не реализовано
- скажу на будущее
- в именами методов проверок - так лучше не делать
- тут - надо четко указать что конкретно проверяем
- во-первых - однозначность очень важна
- во-вторых - даже для такого простого случая - у нас будет не единственная проверка
- так что - важно быть точным в формулировках
- */
- *************************************
- public void deleteTask(String taskName) {
- String focusOnTheTask = "//*[contains(text()," + "'" + taskName + "'" + ")]/..";
- SelenideElement deleteButton = $(By.xpath(focusOnTheTask)).hover();
- String focusOnDelete = "//*[contains(text()," + "'" + taskName + "'" + ")]/../button";
- deleteButton.$(By.xpath(focusOnDelete)).click();
- }
- /*
- обойдись без xpath
- либо вариант продвинутый - tasks.findBy(...) - в списке тасок нашли таску по ее тексту
- либо вариант попроще $(...) - ... - селектор для такой-то таски в списке тасок (обратиться к таске по ее номеру - приемлемо для этой версии)
- то же относится и к кнопке для удаления таски
- к ней тоже - доступись одним из выше описанных способов
- кнопку удаления - лучше идентифицировать по чему-то более наглядному (button - мало что проясняет)
- */
- ****************************************************
- public void clickAll() {
- $(By.className("selected")).click();
- }
- /*
- вот это - не понятно - зачем
- мы в данном сценарии - никуда с all фильтра и не уходили
- ну а на будущее - надо бы для такого метода - имя пооднозначнее
- например - filterAll
- по названию - clickAll() - я не смогла понять - что имелось в виду
- пока не посмотрела на его реализацию
- а чтобы доступиться к элементу http://joxi.ru/L210nzMu64wM0m
- есть куда более наглядные и точные способы
- $(By.linkText(...)) например
- */
- *******************************
- public class BaseTest {
- protected TaskManager testEntity = new TaskManager();
- @BeforeClass
- public static void setUp() {
- open("https://todomvc4tasj.herokuapp.com/");
- }
- @AfterClass
- public static void tearDown() {
- close();
- }
- }
- /*
- предка для тест-класса - тебе точно не нужно
- создавать пейдж-объект в предке класса = скрывать важное
- а это плохо
- создавай пейдж-объект в рамках самого тест-класса
- выносить код в @BeforeClass (=сделать единожды перед запуском всех тест-методов класса)
- когда у тебя всего один тест-метод (именно это просили в задании - реализовать один тест-метод)
- и ты не знаешь - нужно ли будет это в других тест-методах -
- очень преждевременное решение
- строку open("https://todomvc4tasj.herokuapp.com/"); - размести в начале тест-метода
- закрывать вебдрайвер не нужно
- Selenide сам об этом позаботится
- имя пейджа-объекта
- про имя класса для пейджа - уже обсудили
- имя объекта - тоже должно отражать - что это
- если пейдж-объект один - можно назвать переменную page
- можно - отразить что это
- для пейджа класса TodoMVCPage
- будет ок - tasks, todoMVC
- */
- ****************************************
- @FixMethodOrder(MethodSorters.NAME_ASCENDING)
- public class TodoMVCTest extends BaseTest {
- /*
- это вредная практика - диктовать порядок запуска тестов
- тест-методы должны быть независимыми друг от друга
- в данном случае - мы решаем задачу - автоматизировать такой-то тестовый сценарий
- для этого нам нужен один тест-метод - с несколькими шагами
- как писать тест-методы, тестирующие одно конкретное действие - тоже будет на курсе
- разговор тут подлиннее )
- потому - реализуй именно один тест-метод
- также не забывай - что цель тестирования - проверить - что дейтсиве не только выполняется,
- но и выполняется с нужным тебе результатом
- проверки - важны
- и откладывать их не стоит, по крайней мере надолго
- т к нам важно понимать - что конкретно работает не верно
- */
- @Test
- public void test1TaskCreator() throws InterruptedException {
- testEntity.tasksCreator(4);
- }
- /*
- для чего throws InterruptedException ?
- после добавления тасок - нужна проверка
- касается и других действий
- */
- **********************
- @Test
- public void test3MarkTheTaskAsCompleted() throws InterruptedException {
- testEntity.clickAll();
- testEntity.markTaskAsCompleted("task4");
- }
- /*
- не могу понять - зачем clickAll()?
- */
- ***********************************************
- @Test
- public void test4DeleteCompleted() throws InterruptedException{
- testEntity.clearCompleted();
- }
- /*
- не применяй для одного понятия - разные термины
- DeleteCompleted и clearCompleted
- clearCompleted - хороший термин
- он точный и наглядный - т к взят с User Interface
- ниже - ты 2 действия объединил в один метод - complete all + clear completed
- тут - так делать не стал)
- напоминаю - мы пишем один тест-метод с несколькими шагами
- вот для шагов
- complete + clear completed
- complete all + clear completed
- будет приемлемо проверку делать после clear completed
- т е - отложить проверку после закомпличивания на 1 шаг
- т к на all фильтре закомпличивание тасок проверить точно - сложно
- а в целом - правило такое - каждое действие должно быть проверено сразу
- так что - твоя идея объединить эти 2 действия в один блок -
- все же может быть востребованной)
- правда, с учетом того - что вс это мы делаем в одном тест-методе
- */
- @Test
- public void test5MarkAndDelete() throws InterruptedException{
- testEntity.markTaskAsCompleted();
- testEntity.clearCompleted();
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment