Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- по коду
- https://github.com/AleksanderPopov/automician_course/blob/master/src/test/java/com/alexautomician/todomvc/tests/TodoMVCTest.java
- public class TodoMVCTest extends BaseTest {
- ...
- @Test
- public void tasksBasicFlow() {
- @Test
- public void smokeTasksFlow() {
- /*
- не стоило объединять в одном тест-классе эти тест-методы
- на самом деле - в реальном проекте - не было бы смысла держать в одном проекте оба таких е2е теста
- второй вариант - более разумно оставить - т к он оптимальнее
- в нашем случае - это просто разные задания
- разные задания = структурная информация
- а структурную информацию грамотнее фиксировать а уровне пекеджей
- в каждом из которых - свой тест-класс со своим набором методов
- и в таком случае - именовать тест-классы можно исходя из правил
- https://docs.google.com/document/d/13dNyFGbI7mV22UUhH8E0LJ7SzabAmX7Bw7VCHScYfiU/edit#heading=h.xbzelgj7l3up
- т е - пока - для обеих работ - имя класса - TodoMVCTest - будет корректным
- java позволяет в одном проекте держать классы с одним именем - в разных пекеджах
- да и ты вроде бы - предыдущие задания - прячешь в history/
- tasksBasicFlow() - уже история)
- тогда и не будет проблемы с неймингом тест-методов
- smoke - важная информация, да
- но - это НЕ информация о тест-методе (в общем случае)
- это - информация о покрытии (оно может быть реализовано и несколькими тест-методами нескольких тест-классов)
- Это еще мы промоделируем, но чуть позже)
- пока
- 1 - разбей на пекеджи
- каждое задание = свой пекедж
- 2 - у метода smokeTasksFlow() - smoke - лишнее в имени
- tasksFlow, commonTasksFlow - будет OK
- */
- @Test
- public void addEditDeleteCompleted() {
- @Test
- public void editDeleteCompleteClearActive() {
- @Test
- public void editActivateClearDeleteAll() {
- /*
- методы - явно не фиче-тесты получились
- это - тоже е2е-ы
- разберем отдельно
- и сначала на тест-плане
- */
- **********************************
- @Test
- public void smokeTasksFlow() {
- page.add("1")
- .toggle("1");
- /*
- тут - нужна проверка текстов тасок
- проверим, что на all таски отображаются вне зависимости от статуса
- а проверка после перехода на Active - лишь допроверит факт закомпличивания
- и хорошо проверит - фильтеринг
- */
- page.goActive()
- .shouldBeEmpty()
- *************************************
- private SelenideElement editingTask = $(".editing .edit");
- /*
- не, это - не editingTask )
- это поле для ввода нового текста таски
- да и вообще эта переменная - не нужна
- полезно придерживаться вот такой схемы
- есть у нас переменная для коллекции тасок - tasks
- и если нужно доступиться к чему-то из этой структуры - то отталкиваться нужно от этой переменной
- а не использовать новый независимый селектор
- что это нам дает
- наглядность кода
- сравни
- $(".editing .edit")
- и
- tasks.findBy(...).find(...)
- во втором случае - суть длиннее
- но - нагляднее и понятнее - с чем мы работаем
- по сути - что нам нужно получить
- нам нужно получить
- таску с классом editing
- а в ней - внутренний элемент с классом edit
- это и будет описано во втором варианте
- помимо наглядности, так ты получишь код, в котором будет меньше независимых селекторов
- что в долгосрочном периоде - помогает проще поддерживать код
- вот поменялось приложение
- и чем меньше независимых селекторов - тем меньше придется адаптировать код
- еще приятный бонус в Selenide - при использовании вот такого варианта tasks.findBy(...).find(...)
- (по сравнению с независимым селектором)
- код ошибки будет более точным
- будет жадоба на какую-то конкретную часть этого выражения
- что тоже удобно
- в общем - очень рекомендую - отталкиваться от сущности, для которой уже объявлена переменная
- и продвигаться от общего к частному
- */
- ****************************
- public TaskPage editEnter(String fromTaskName, String toTaskName) {
- tasks.findBy(exactText(fromTaskName))
- .doubleClick();
- editingTask
- .setValue(toTaskName)
- .pressEnter();
- return this;
- }
- public TaskPage editCancel(String taskName) {
- tasks.findBy(exactText(taskName))
- .doubleClick();
- editingTask
- .setValue(taskName)
- .pressEscape();
- return this;
- }
- /*
- посмотри - как похожи методы
- вместо того, чтоб заводить переменную editingTask
- лучше реализовать метод
- SelenideElement startEdit(String fromTaskName, String toTaskName)
- в котором будет происходить все вплоть до ввода нового значения
- и метод будет возвращать SelenideElement = элемент в котором новое значение было введено
- чтоб потом вызывать такой метод
- startEdit(fromTaskName, toTaskName).pressEnter();
- по поводу реализации и набора параметров editCancel(String taskName)
- тут вся соль отмены редактирования в том - что мы УЖЕ ввели новое значение
- и нажав ескейп - вернули старое значение = отменили
- не надо убирать второй параметр toTaskName - это искажает смыслы
- имена методов
- editEnter - я бы в данном случае - не уточнялась до энтера - т к это стандартная реализация редактирования
- как доберемся до автоматизации additional edit operations - там уже будем уточняться
- editCancel - тоже с недостатками
- что мы делаем = отменяем редактирование = cancelEdit
- а если хочется подчеркнуть последовательность действий - startEditThenCancel
- что-то такое
- я приверженец первого варианта
- вот сюда взгляни
- https://docs.google.com/document/d/13dNyFGbI7mV22UUhH8E0LJ7SzabAmX7Bw7VCHScYfiU/edit#heading=h.o7vsh4r93b55
- */
- ******************************************
- public TaskPage toggleAll() {
- $("#toggle-all").click();
- return this;
- }
- public TaskPage completeAll() {
- $("#toggle-all").click();
- return this;
- }
- public TaskPage toggle(String taskName) {
- tasks.findBy(exactText(taskName)).$(".toggle").click();
- return this;
- }
- public TaskPage activate(String taskName) {
- return toggle(taskName);
- }
- /*
- посмотри на реализацию методов
- toggleAll и completeAll
- и
- toggle и activate
- в каждой паре - оба метода делают одно и то же
- но те же самые действия - называют по-разному
- в паре toggle и activate - ты выкрутился более DRY
- но все равно - так нельзя
- вызови activate для активной таски
- что произойдет?
- а completeAll() для ситуации, когда все таски и так закомпличены?
- будут выполнены противоположные действия
- а имя метода - должно точно отражать что он делает
- вне зависимости от контекста его вызова
- иначе - это будет путать
- посмотри перевод toggle = переключить
- и этот термин - корректный и для случая с закомпличиванием, и для случая с активацией
- тут есть варианты вот какие
- простой, лаконичный
- оставить в пейдже - только методы toggle & toggleAll
- и применять их и для закомпличивания, и для переоткрытия
- и в коде, при вызове - кратко комментировать - что это
- второй, более сложный в реализации, более тормознутый в использовании
- но с более красивым набором методов
- реализовать не toggle & toggleAll
- а
- complete
- activate
- completeAll
- activateAll
- чтоб методы отвечали своим именам - нужно встроить в них перед выполнением действия -
- проверки
- чтоб действие просто не могло выполниться - если таски в не верном состоянии на начало операции
- тут проверка будет работать - не как проверка тестовой логики
- а как инструмент, который обеспечивает только правильную работу метода - метод будет корректно работать
- только в правильном контексте
- в этом простом приложении - я бы пошла первым путем
- в более сложных вариантах - предпочла бы второй
- цель - меньше всего держать в голове на этапе написания самого теста
- */
- *******************************
- public TaskPage shouldHaveCompleted(String taskName) {
- tasks.findBy(text(taskName)).find(".toggle").shouldBe(selected);
- return this;
- }
- /*
- я бы не выносила это как отдельную проверку, если честно
- кстати - вот тебе - хорошая идея для проверки
- которую можно встроить в activate (в complete - будет что-то похожее )
- и я бы везде - где получала таску по ее тексту - использовала один и тот же кондишен - exactText
- и точно, и одинаково
- чтоб потом не маяться
- не ловить странные ошибки
- (например выполняешь проверку для "ask", а она выполняется в реальности - для "task1", которая идет раньше по списку)
- не думать - почему тут так получаем таску, а тут эдак. Однозначный код - всегда лучше. Если делаем так-то - то делаем так-то
- тут разнообразие - наш враг
- */
- *********************************************************
- public TaskPage shouldHaveCompleted(String... taskNames) {
- for (String taskName : taskNames) {
- shouldHaveCompleted(taskName);
- }
- return this;
- }
- /*
- сравни
- этот цикл
- и такой вариант
- tasks.filterBy(...).shouldHave(exactTexts(taskNames));
- в любом случае - второй вариант - лучше - т к эффективнее (1 ждущая проверка вместо нескольких) и проще
- если можно обойтись без циклов - надо обходиться
- тут - тоже
- такая проверка - не нужна
- но какие-то тут описанные идеи - можно применить для реализации
- completeAll и activateAll
- */
- ************************************
- public TaskPage shouldBeItemsLeft(int i) {
- /*
- избегай однобуквенных имен переменных
- только для итераторов цикла это ок
- https://google.github.io/styleguide/javaguide.html#s5.2.6-parameter-names
- сравни
- shouldBeItemsLeft
- shouldItemsLeft
- мне кажется - второй вариант - попонятнее
- ?
- */
- *****************************************
- public TaskPage goAll() {
- $("#filters>li:nth-of-type(1)>a").click();
- tasks = $$("#todo-list>li");
- return this;
- }
- public TaskPage goActive() {
- $("#filters>li:nth-of-type(2)>a").click();
- tasks = $$("#todo-list>li.active");
- return this;
- }
- public TaskPage goCompleted() {
- $("#filters>li:nth-of-type(3)>a").click();
- tasks = $$("#todo-list>li.completed");
- return this;
- }
- /*
- по первым строчкам методов
- т к приложение простенькое - можно вполне обойтись вариантом
- $(By.linkText(...))
- для приложения посложнее я бы применяла
- $$("#filters>li").findBy(exactText("..."))
- а т к $$("#filters>li") - повторялся бы трижды
- то вынесла бы $$("#filters>li") - в переменную пейджа
- по второй строчке
- это зло)
- ты добавил магии, и сильно
- да и проверки не обеспечил точные
- ведь - проверяя коллекцию - $$("#todo-list>li.active")
- мы не гарантируем - что других тасок, которые в эту коллекцию не попадают - не видно в списке
- тут нужно было - оперировать $$("#todo-list>li").filter(visible)
- еще раз посмотри - что я писала насчет проверок
- может пригодиться вот это
- http://joxi.ru/l2ZNaR0F83gJv2
- Посмотри видео Якова про это - https://drive.google.com/file/d/0B8hgIBw8-V-AdGxxU1R3enl1RzQ/view?ts=567ab8d7
- чуть больше пояснений
- мы можем быть максимально точными и держать 4 проверки
- 2 -
- в списке = такси с такими-то текстами
- в списке = пусто
- и еще 2 -
- в отфильтрованном по visible списке = таски с такими-то текстами
- в отфильтрованном по visible списке = пусто
- И за точность будем платить тем - что надо думать - когда какую проверку вызвать правильнее
- и если это делать бездумно - то при небольших изменениях сценариев - могут тесты падать на проверках,
- или второй вариант - не будем нормально пользоваться полученной точностью...
- мы можем исходить из того, что ошибку, когда невидимые таски копятся в списке - мы тестим на более низком уровне,
- и на UI уровне - нам не нужно до этого уточняться. Поэтому - мы будем держать всего 2 проверки
- в отфильтрованном по visible списке = таски с такими-то текстами
- в отфильтрованном по visible списке = пусто
- В таком случае - каждый раз понятно - какую проверку вызывать - получаем более KISS картину
- правда, платим за это точностью) Но - возможно - если мы уже отдельно это в тестах покрыли -
- что у нас не копятся невидимые таски - так мы и не платим ) И - тогда - все проще в написании тестов,
- и в их сопровождении.
- Тогда - поскольку обе проверки реализованы одинаково и других нету - можно из имен проверок скрыть этот нюанс
- и назвать их assertTasks и assertNoTasks (хотя в них работаем с отфильтрованным по visible списком тасок)
- почитай
- http://enterprisecraftsmanship.com/2016/09/29/law-of-demeter-and-immutability/
- http://enterprisecraftsmanship.com/2016/05/12/immutable-architecture/
- а чтоб уберечь себя от мутабельности свойств объекта - можно их как final объявлять
- даже более - так технически грамотнее делать)
- просто обычно - на уровне тестов - уже многие считают это задротством
- я попробовала на более серьезных проектах
- мне идея с final - понравилась)
- */
- ************************************************
- по тест-плану
- https://docs.google.com/spreadsheets/d/1eAw67UrF58vyCf0Fk7f9hzoFFICgkeYVhhrph4XSb5g/edit#gid=0
- /*
- мы - по-прежнему - планируем smoke
- это значит покрываем только высокоприоритетное и ТОЛЬКО на одном контексте
- и еще - один фиче-тест - проверяет одну фичу
- лишь одну
- фактически - разгрузив наш е2е (что ты успешно сделал)
- надо было допокрыть фиче-тестами
- http://joxi.ru/Rmzqpx8H00kn1r
- 3 штуки - для каждого действия
- и в рамках проверок в фиче-тестах - второй проверкой - покрой проверку items left
- просто потому что по пути и это почти ничег не стоит
- у тебя реализованы - не фиче-тесты
- а небольшие е2е тесты
- учти по оформлению фиче-тестов
- http://pastebin.com/4TiQi6jR, строки 51-70
- и
- это
- имя фиче-теста - что тестим и на каком фильтре
- структура фиче-теста
- предварительные действия
- тестируемое действие
- проверки
- предварительные действия начнем с комментария //given - ...
- чтоб было понятно - что это предварительные действия и что за ситуацию мы в результате их получим
- внутри и в конце блока предварительных действий - проверок не делаем
- (мы это тут не тестируем, а используем для создания тестовой ситуации, ниже будет подробнее)
- после предварительных действий - пропустим строку
- чтоб выделить - вот подготовка, вот - тестируемое действие
- проверки
- сначала - более важные
- затем - менее важные
- (собственно - так ты и реализовал)
- такой порядок - чтобы даже если тест упадет на менее важной проверке - был фидбек о важной проверке
- еще - в фиче-тестах мы можем себе позволить более интересные тестовые ситуации
- например - редактирование второй таски в списке
- Это к общему сведению)
- Есть разные способы выполнять предварительные действия
- Мы сейчас делаем это через действия на UI (User Interface)
- А есть еще методы - работать непосредственно с данными (далее вы такое тоже попробуете)
- Так вот через действия на UI - предварительные действия не быстрые и часто не достаточно надежные
- А через непосредственную работу с данными - предварительные действия быстрые и надежные
- Если предварительные действия медленные или не надежные
- То проверка в конце предварительных действий нужна
- А если мы уверены - что после предварительных действий гарантировано все ОК,
- то и проверок не надо после предварительных действий
- Но, поскольку наше приложение - простое
- Разумно не делать проверку в конце предварительных действий
- чтобы наши тесты были эффективнее
- Тестировали бы что-то типа соцсети и если бы предварительные действия были
- реализованы через UI - да, после предварительных действий было бы разумно
- выполнить проверку (проверка после предварительных действий нам позволяет отличить -
- ошибка возникла на этапе выполнения тестируемого действия, или все же раньше)
- */
- **********************************************************************************************
- =========================================================================================
- 1. "оба варианта, которые ты привел - одинаково ненадежны)
- $$(tasks) - лишь рассказ про то - как искать коллекцию
- $$(tasks) - мы начинаем ее обход, и получим коллекцию, которая есть на этот момент
- ты знаешь универсальный способ определить - что коллекция догрузилась?
- любая коллекция, сферическая в вакууме )"
- >>>>>>>> ух, тяжеловато было это понять)) посмотрел вебинары по sdet, вроде
- понятнее стало. типа если мы хотим чего то подождать от списка конкретного,
- то будем писать $$().shouldHave(size(7)) - ждать пока элементов 7, и
- только потом работать? и метод get(index) Должен по идее тоже ждать
- размера листа...правильно я понимаю теперь?
- /*
- да, если ты планируешь работать со списком - через обход коллекции - таки да - нужна проверка перед этим
- уже писала - мне ни разу пока не пригодилось не в отладочных целях - делать такой обход)
- а вот варианты $$(...).get(...) $$(...).find(....) - тут уже предварять это проверками не нужно
- т к при начале работы с таким элементом - Selenide будет ждать его видимости и так
- */
- =========================================================================================
- 2. "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 ?
- /*
- мы и сейчас, и далее везде на курсе - пишем функциональные тесты)
- и да, верно, закомпличивание можно проверить тобой описанными способами
- этого касалась и в ревью на твой сценарий
- думаю, уже вопросы должны были отпасть
- */"
- >>>>>>>> у меня сейчас есть метод
- public TaskPage shouldBeEmpty() {
- tasks.shouldBe(empty);
- return this;
- }
- разве его нужно удалять? если я бегаю по фильтрам, и мне нужно
- проверить что фильтр поменялся (например с Active на Completed)
- и нет ни одной закомпличеннной таски, как мне это сделать,
- если не смотреть размер тасок?
- /*
- а я и не предлагаю удалить метод shouldBeEmpty()
- мне не нравятся методы - где ты фильтруешь список тасок по классу active / completed
- это - как раз бессмысленно - т к не обеспечивает проверки = мы видим такие-то таски на таком-то фильтре
- на active и completed фильтрах - разумно проверять $$("#todo-list>li").filter(visible)
- на каждом из фильтров - мы видим лишь часть из всех тасок
- это и нужно проверить
- а какой класс у тасок - active / completed - это уже больше UI детали
- конечно - если надумаешь реализовывать методы
- complete & activate
- completeAll & activateAll
- вот в них - внутри этих методов - таки разумно использовать проверки основанные на работе с классами active / completed
- там это - будет уместно
- */
- =========================================================================================
- 3. ""не понимаю ценности BaseTest поясни, что из реализованного в BaseTest - ценно"
- >>>>>>>> ну как минимум он нужен для 'Configuration.browser = "firefox";'
- /*
- по умолчанию - так оно и есть)
- потому - ничего и не нужно делать
- */"
- >>>>>>>> я знаю что по умолчанию так и есть, но явные указывания таких
- глобальных переменных в конфиге необходимо, как по мне) чтобы мне
- не надо было вспоминать в каком там классе эти настройки хранятся
- в селениде, а чтоб у меня это было все в моем проекте)
- про дефолт - в pom Тоже можно ставить last version, но это чревато
- нехорошими последствиями)
- не хочу чтоб обновление либы могло повлиять на мои тест конфигурации
- /*
- ну, аналогия не совсем удачная, по моему мнению
- я бы в поме тоже про ласт версию ничего не писала)
- но ок
- я принимаю доводы)
- как весомые)
- */
- =========================================================================================
- 4. "Я бегло тебе ответила
- http://joxi.ru/BA0p30gsJJbq7A (edited)
- 1 - начни тут фразу с page .
- будет нагляднее
- 2 и далее - не хватает проверок"
- >>>>>>>> 1 - провтыкал, исправил. 2 и далее - 2 не стал трогать, т.к. не понимаю
- что именно тут проверить. мы проверяем что таска закомпличена переходя
- в active и проверяя что там пусто. то что далее исправил)
- /*
- это описала выше
- тут повторюсь
- такая проверка даст
- проверим = на олле - таски видны с любым статусом
- четче зафиксируем было-стало - какой список был до перехода на фильтр и после
- да, закомпличивание допроверится аж проверкой после перехода на фильтр, но предыдущие моменты - ценны
- потомы стоит проверку сделать
- */
- =========================================================================================
- 5. посмотрел видео Якова https://drive.google.com/file/d/0B8hgIBw8-V-AdGxxU1R3enl1RzQ/view?ts=567ab8d7
- по поводу assertTasks and assertVisibleTasks, мой подход разве не лучше его альтернативы?
- метод все еще один, и он ищет таски в зависимости от текущего фильтра свои
- public TaskPage goAll() {
- $("#filters>li:nth-of-type(1)>a").click();
- tasks = $$("#todo-list>li"); // все таски
- return this;
- }
- public TaskPage goActive() {
- $("#filters>li:nth-of-type(2)>a").click();
- tasks = $$("#todo-list>li.active"); // только активные
- return this;
- }
- public TaskPage goCompleted() {
- $("#filters>li:nth-of-type(3)>a").click();
- tasks = $$("#todo-list>li.completed"); // только закомпличенные
- return this;
- }
- /*
- не, твой подход не лучше
- выше писала почему
- пример
- верно проставляются классы таскам, но при переходах на любой из фильтров - они видны
- твои проверки - пройдут )
- ну и про то, почему не стоит делать свойства объекта мутабельными - почитай
- это и правда стоит учитывать при проектировании
- хорошая практика, сильно облегчающая жизнь
- код проще, предсказуемее
- его легче писать, легче менять, легче сопровождать
- */
- =========================================================================================
- 6. еще вопрос, можно ли это как то нормально сделать (тот метод что переменное число аргументов
- принимает) ?
- public TaskPage shouldHaveCompleted(String taskName) {
- tasks.findBy(text(taskName)).find(".toggle").shouldBe(selected);
- return this;
- }
- public TaskPage shouldHaveCompleted(String... taskNames) {
- for (String taskName : taskNames) {
- shouldHaveCompleted(taskName);
- }
- return this;
- }
- /*
- Писала выше и про метод
- и про то - где такая проверка была бы уместной
- */
- =========================================================================================
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement