Перейти к содержимому

Тестирование безопасности
онлайн, начало 16 июня
Автоматизатор мобильных приложений
онлайн, начало 16 июня
Автоматизация тестирования REST API на Python
онлайн, начало 16 июня
Selenium WebDriver: полное руководство
онлайн, начало 18 июня

Noksa

Регистрация: 23 мая 2018
Offline Активность: 29 дек 2018 05:43
-----

#166914 Загрузка документа

Написано Noksa 02 июля 2018 - 09:42

Добавлю лишь, что реализуя проверку локального диска вы получаете совершенно не контролируемую систему, так как вы не знаете о том, что ответил сервер при нажатии на кнопку, были ли в процессе скачивания какие либо проблемы, как долго ждать до завершения скачивания и тому подобные проблемы которые от вас потребуют такое кол-во костылей, что полагаться на результат теста станет невозможно.

 

Не буду вас переубеждать в этом.

 

Вы не понимая проблемы пытаетесь автоматизировать условия её воспроизведения, вместо того, чтобы разобраться в сути и автоматизировать каждую часть процесса необходимым инструментарием.

 

 

Очень странное у вас мышление, честно говоря.

У меня кейс E2E. А вы мне предлагаете зачем-то тестировать саму возможность скачивания. Похоже это вы не понимаете о чем речь идёт.

 

Как я могу не знать, что ответил мне сервер, нажимая на кнопку, если я после нажатия этой кнопки вполне конкретно ожидаю начала скачивания файла и заведомо знаю, что должно быть внутри него, включая его размер?

 

Покрывать юнит-тестами саму возможность скачивания должен разработчик, это он разработал эту функцию. Не надо переваливать это на плечи автоматизатора.

 

Интересно, а "Как долго ждать до завершения скачивания по клику" чем-то отличается от "Как долго ждать до завршения скачивания по прямой ссылке"?

 Так-то это вообще не константа, может у вас вообще интернета не будет в момент скачивания. 

 

У меня нет ни одной проблемы пока что со скачиванием файлов через клик по кнопке, и я ни секунды лишней не жду.

Все тесты прекрасно могут хоть в 100 потоков параллельно качать файлы в одну и ту же папку и не мешать друг другу.

Если для вас это проблема и нужно городить костыли, то не надо это проецировать на других.

 

А вот если скачивание по кнопке отломается, но будет работать по прямой ссылке, о которой пользователь естественно не знает - что вы будете говорить в этом случае? Что само скачивание работает? Ну так а толку от этого, если пользователь это скачивание запустить не может?

 

Как выше уже писали - проверить что скачивание по кнопке работает - достаточно один раз, если оно вызывается из одного и того же места.


  • 1


#166764 Загрузка документа

Написано Noksa 22 июня 2018 - 12:30

Спасибо за советы, в принципе картина ясна. Мне самое главное было понять как сделать правильно, и не городить огородов.   

 

Роман правильно написал - проверять генерацию и содержимое файла лучше через юнит-тесты - это должны делать разработчики.

Если такие юнит-тесты не написаны, значит разработчики забивают на юнит-тесты.


  • 1


#166760 Загрузка документа

Написано Noksa 22 июня 2018 - 11:11

Опять же, а если файл не стал скачиваться? Сколько его ждать? И если ждать то получается нужно опять принудительно выставлять максимальное время ожидания... 

 

 

Если файл не стал скачиваться, то это дефект, со всеми вытекающими. Сколько ждать его - это вам должно быть известно, у меня на проекте скачивание начинается в пределах 3 секунд, я это время и жду.

Если файл начал качаться позже - это уже дефект.

Если один тест зафейлился по этой причине - можно сделать маркер, чтобы другие не запускались, если сломан механизм скачивания файла.

 

У вас в любом случае при почти любых проверках, где результат появляется не моментально есть неявные ожидания. Почему же вас это ожидание смущает именно при скачивании файла, ведь это такая же абсолютно проверка?

 

Либо воспользуйтесь советом человека выше.


  • 1


#166755 Загрузка документа

Написано Noksa 22 июня 2018 - 06:31

Александр спасибо за ответ.

В том то и дело что файл может быть не скачан. Кликаем на download а в ответ тишина. 

 

Насчет подождать условные 2-3 секунды - вот тут то и основная загвоздка, что по моим наблюдениям файл начинает скачиваться как мгновенно после нажатия кнопки, так и через 10 секунд :)),  да да через 10(редко, но бывает и дольше), почему так это не ко мне, имеем что имеем, по этому ставить в "лоб" ожидание 10 секунд для каждого моего документа (а их около 200) не совсем камильфо, собственно я и думал что есть уже какое-то "умное" решение этого ожидания. 

 

В любом случае, спасибо что отозвались.

 

Ну здесь по-другому не получится.

Да и это не совсем в лоб.

Вы же не будете ждать каждый раз 10 секунд. Если файл начал качаться за 1 секунду - то ожидание прервется и начнется другое - ожидание завершения скачивания файла.

 

Если у вас файлы начинают качаться спустя 10 секунд после клика по кнопке - это проблема разработки, надо идти к ним/к начальству и просить оптимизировать. 

 

Предполагаю, что после нажатия кнопки "Скачать" начинается формирование файла, и после того как он сформируется - начинается скачивание, отсюда и различие в ожидании.


  • 1


#166745 Как записать переменную внутри локатора

Написано Noksa 21 июня 2018 - 18:31

Java:

WebElement parentElement = driver.findElement(By.xpath("//div[text()='" + st1 + "']/../.."));

C#:

var parentElement = driver.FindElement(By.XPath($"//div[text()='{st1}']/../.."));

  • 2


#166570 Page Object ver.2

Написано Noksa 11 июня 2018 - 18:28

Скажу от себя.

 

 

 

создаем маленькие методы (например заполнить поле, выбрать с селекта)

 

Предположим, что у вас будет на странице 20 полей, которые нужно заполнить и 5 селектов, которые нужно выбрать.

 

Если вы будете для каждого поля делать отдельный метод, то количество методов на данной странице, помимо каких-то вспомогательных и унаследованных, будет большое количество и это не очень удобно. Количество методов не должно быть слишком большим, это очень неудобно.

 

Здесь можно пойти другим путём. Сделать один универсальный метод, например "fillField", который будет принимать в себя два аргумента: enum и string.

Где enum - это перечисление всех полей\селектов, которые есть на странице, а string - соответственно чем нужно заполнить\что нужно выбрать.

 

Ну а в теле метода уже через switch реализовать заполнение\селект конкретных полей\селектов.

 

В итоге у вас будет всего один метод, который работает с полями\селектами на странице.

 

Далее.

 

 

 

Жду конструктивной критики

 

Я сторонник PageFactory с кастомным локатором\декоратором, поэтому для меня поиск элементов через findElement и поля с типом By - это некрасиво и глаза разбегаются.

 

Могу привести пример, почему PageFactory лучше, на мой взгляд.

Вы можете придумать свои собственные атрибуты (аннотации), которые можете использовать у каких-то элементов.

Есть, допустим, элемент, который появляется динамически, но точно должен появиться спустя 3 секунды.

 

При поиске через чистый findElement, у вас тут же будет выброшено исключение, если элемент не существует или исчез из DOM страницы.

Конечно же, вы всегда можете обернуть вызов findElement для достижения нужного результата - но это тоже на мой взгляд не оптимально.

 

А при использовании PageFactory и своей собственной аннотации, вы можете указать, что конкретный элемент нужно ждать 3 секунды. И если спустя эти 3 секунды он не появился - тогда будет выброшено исключение.

Либо вы можете добавить аннотацию, чтобы не выбрасывалось исключение, а просто писался варн в лог и тест шёл дальше - словом, всё что захотите.

 

Пример с C#:

[FindBy(Id = "date_from")]
[ElementTitle("Операции с")]
[TimeToSearch(3)]
private ADatePicker _dateFromPicker;

При обращении к этому элементу, будут предприняты попытки его поиска в течении 3 секунд. 

Или можно использовать не хардкодное значение, а ссылаться на какое-то проперти, и при его изменении оно соответственно изменится у всех затронутых элементов:

[FindBy(Id = "date_from")]
[ElementTitle("Операции с")]
[TimeToSearch(App.Properties.Timeouts.Small)]
private ADatePicker _dateFromPicker;

А так же задавать нужный polling time, да в общем-то что угодно. 

Я, например, реализовал атрибут (аннотацию) для указания родительского элемента:

[FindBy(Id = "date_from", ParentElement = dateBlockName)]
[ElementTitle("Операции с")]
private ADatePicker _dateFromPicker;

Чтобы избежать дублирования XPath.

 

 

Далее.

 

 

 

Создал класс BaseTest, в котором у меня реализована инициализация драйвера, также убрал SetProperty с явным указанием в нем пути к драйверу заменив его dependency WebDriverManager в pom файле. В итоге получился такой код:

 

Хардкод хромдрайвера и до сих пор есть необходимость всюду пропихивать экземпляр драйвера.

 

 

 

Мне кажется что в тестовых классах я не должен иметь возможности делать driver.findElement, driver.click и т.д. Тоесть на прямую обращаться к драйверу. Или я ошибаюсь?

 

И да и нет.

У вас, как ни крути, будет доступен драйвер в тестовом классе. У вас же тестовый класс наследуется от базового, в котором драйвер protected.

Просто не используйте драйвер в тестовых классах в таких целях.

 

 

 В архитектуре классов у меня есть также класс SummaryAPI. В лекции что я смотрел, от этого класса неследовались классы BasePage и BaseTest - тоесть в них была реализация чего то общего для этих двух классов, например того же метода find:

 

Могу сказать по себе. 

Мне пришлось форкнуть Allure под C# и реализовать в нём возможность создания\добавления шагов и аттачей в [setup] и [teardown] методах.

 

И чтобы не указывать в каждом тестовом классе, что этот класс должен генерить отчет, я наследую базовый тестовый класс от класса, в котором у меня реализован старт\стоп отчета и прочее.

В итоге для всех тестовых классов у меня будет создаваться отчет.

public abstract class ABaseTestConfig : AllureReport
    {
      ...
    }

Но это что касается базового тестового класса.

 

Для чего нужно иметь общий базовый класс и для базового тестового класса и для BasePage - честно говоря никогда не думал об этом.

 

Могу лишь предположить, что какие-то общие данные текущего запуска тестов \ теста, но вроде все (?) тестовые фреймворки и так это хранят в доступных через статичные классы контекстах. 


  • 1


#166523 Наследования

Написано Noksa 08 июня 2018 - 07:12

Так же, я бы вам советовал сразу писать такую архитектуру, в которой не требуется в конструктор страницы передавать драйвер.

Т.е, в конструктор страницы вообще ничего не следует передавать.

 

Как вариант, использовать статичный драйвер в контейнере ThreadLocal<>.

 

Это моё субъективное мнение.

 

 

Пример с C#, на страницах не требуется объявлять конструктор, он вызывается из базового класса.

При этом все прекрасно бегает в параллельных потоках.

public class NewsPage : BasePage
    {
      ....
    }


public abstract class BasePage
    {
     ...

     protected BasePage()
        {
            Elements.Clear();
            ElementsInBlocks.Clear();
            PageFactory.InitElements(DriverManager.Driver, this, new AFieldDecorator());
        }

  • 1


#166522 Наследования

Написано Noksa 08 июня 2018 - 06:55

Вам нужно вынести всю логику по работе с драйвером в базовый класс для всех тестов. Всё создание драйвера и его уничтожение будет описано в одном месте.

От него и надо будет наследовать классы тестов.

 

В идеале это должна быть фабрика по созданию нужного драйвера.

В классах тестов не должно быть никакого упоминания о драйвере!

 

Тут я Вас понял! Именно из за того что я создавал драйвер в каждом классе у меня и наследование не получится. 

Фабрики для создание драйверов мне не нужно - работаем только с chrome, поэтому и указал путь на ЖД. Но уже видел ссылку где это делаеться через maven, пересмотрю потом.

И да то, что в тестах не должно быть упоминание о драйвере только сейчас понял. Но так как я только начинаю работать то для меня понимание этого уже плюс)

 

 

Мыслите шире. Или даже так - мыслите интерфейсами. Это позволит вам писать легкоизменяемый код, который при этом не повлияет на работоспособность другого кода, который его использует.

 

Сейчас не нужно - а завтра будет нужно. Заказчики люди непостоянные и требования у них могут меняться каждый день :)

 

Лучше сразу предусмотреть возможность создания любого драйвера, чтобы потом не пришлось строить костыли.

 

Хороший вариант, это параметризация запуска тестов. При запуске передавать параметр - какой браузер использовать.

Таким образом, вы одновременно, например на разных машинах, сможете запустить все тесты на разных браузерах.

 

 

Дело в том что как бы я не хотел сделать тесты независимыми - этого у меня не получиться. Возьмем для примера заполнения этой карточки. От этого теста зависит выполнение всех последующих. В конце теста есть клик на кнопку, после которого отправляеться на емейл ссылка для дальнейшей работы. Поэтому если даный тест не отработает (ссылка не прийдет) то и последуюющие шаги мне выполнять не нужно. Я не смогу начать выполнять тест с 3 или 4 этапа бизнес процесса. Только в такой последовательности. Поэтому даже при запуске, для того что бы проверить например 8 сетод (завершения БП) мне нужно тест запустить с самого начала. 

 

Можно использовать API, или БД, необязательно создавать тестовые данные через интерфейс. Это быстрее и надежнее.
 
Но, если вы прям уж очень хотите завязываться на интерфейс - в таком случае нужно скипать все тесты, которые не будут выполняться без этого шага, чтобы сэкономить время. Ибо запускать их смысла нет. Ну и указывать причину, почему скипнуты.
 

 

 

На сколько я понял вы предлагаете уже в тестах создать метод напримет cardFill() в нем создать обьект OrgCard  и на нем вызвать метод orgcard.setFieldsOnTheOrgCard(); 

 

Не-не, ни в коем разе.
Да, у вас вполне себе pageobject.
Тесты не должны создавать методы, они должны только использовать их. Иначе будет каша.
 
 

 

 

Для первого класса у меня такая же реализация. А для других классов  я почему то решил что нужно делать наследования. То есть Вы говорите что нужно и для других классов создавать обьект класса OrgCard  и на нем вызвать метод orgcard.setFieldsOnTheOrgCard(), а не делать наследование

 

Вы путаете тестовые классы и PageObject классы.

Тестовые классы должны использовать экземпляры pageobject классов.

Сам тестовый класс не должен знать ничего о другом тестовом классе, он должен только наследовать базовый тестовый класс для всех тестовых классов, в котором реализована общая логика для всех тестовых классов (методы создания драйвера, уничтожения драйвера, что-то ещё)

 

Так же как и PageObject классы - могут наследовать какой-то базовый для них всех класс, в котором например:

1) Будет конструктор с PageFactory с кастомным декоратором, чтобы не писать его каждый раз в каждом наследнике.

2) Какие-то общие для всех страниц методы и так далее...

 

 

PS: 

Вынесите в OrgCard всю логику, ведь она относится непосредственно к этой странице:

public string fillCard()
{    
    this.setFieldsOnTheOrgCard();
    PopUpMessage popUpMessage = this.createButtonClick();
    return popUpMessage.getLinkFromPopUp();
} 

 

А в тестах используйте в первой строке:

new OrgCard(driver).fillCard()

и никакого наследования тестами других тестов.


  • 2


#166519 Наследования

Написано Noksa 08 июня 2018 - 04:38

 

 

 а вот наследовать тесты - плохая практика.

 

Вот здесь я не понял Вас немного. Каждый методу меня это этап бизнес процесса. Вот cardFill  - это заполнение карточки заявления. В итоге я хочу на одной ветке БП создать этот метод (так как я и сделал в классе FirstBP_OrgCreating) , а дальше наследовать все классы от этого класса, и просто вызывать этот метод. Я не могу понять чем это чревато?

 

 

1) Ваши классы нарушают SRP в чистом виде.

Они не выполняют одну задачу, а выполняют две - проверяют какой-то функционал и создают драйвер.

 

Вам нужно вынести всю логику по работе с драйвером в базовый класс для всех тестов. Всё создание драйвера и его уничтожение будет описано в одном месте.

От него и надо будет наследовать классы тестов.

В идеале это должна быть фабрика по созданию нужного драйвера.

В классах тестов не должно быть никакого упоминания о драйвере!

 

Сейчас у вас создание драйвера, во-первых, имеет хардкод в жесткий диск (это вообще жесть полная), во-вторых - предусмотрено создание только хромдрайвера.

И, опять же, дублирование кода создания драйвера, при этом один класс наследует другой... выглядит странным.

 

 

Чем всё это чревато? А тем, что если у вас поменяется что-то в создании драйвера, например, вам будет нужен не хром, а лиса - вам придётся это менять во всех местах.

Эту проблему вообще надо решать другим путём, конечно, это просто как пример.

 

2) Сейчас у вас заполнение карточки является по сути не тестом, а precondition шагом. 

В той реализации, что у вас есть - у вас все тесты будут зависеть по факту от одного теста. Зависимые тесты - это очень плохо. Если ваш первый тест не будет работать - все остальные тоже не будут работать из-за того, что они не могут выполнить precondition шаг.

 

В идеале, нужно уходить от такого рода шагов. Например, путём создания другого precondition шага, который будет в БД приложения записывать нужные данные, а тест уже будет их использовать.

Таким образом, если создание precondition шага в интерфейсе не будет работать - вы сможете проверить другой функционал, минуя его.

А в отдельном одном тесте уже проверять непосредственно cardFill через интерфейс.

 

 

Действия на страницах надо выносить не в базовый класс, а следовать PageObject паттерну, и выносить это в отдельные методы на конкретном pageobject'е. А в самих PageObject применять композицию для избежания дублирования кода.

Допустим, есть общее меню для всех страниц - его нужно выносить в отдельный класс (я их называю блоками) и используя композицию вносить его во все pageobject, где он присутствует.

Таким образом, элементы описанные в этом блоке - будут описаны в одном месте, и при их изменении вам нужно будет исправить их только в одном месте, а не на всех страницах.

 

 

Общее видение PageObject:

 

У вас есть страница Cards. 

Создаёте класс Cards, в нём создаете мелкие методы, которые делают что-то на этой странице.

Потом, если вас это не устраивает - делаете там же общий метод cardFill, который содержит в себе вызовы мелких методов, а не дублирует их логику.

 

 

А в тесте уже используете этот класс и методы из него. Здесь не нужно наследование.


  • 2


#166405 Извлечь текст из определённого места на странице С# + webdriver

Написано Noksa 31 мая 2018 - 07:21

Ну и да - я вам советую использовать PageObject паттерн, если вы его не используете. 


  • 1


#166402 Извлечь текст из определённого места на странице С# + webdriver

Написано Noksa 31 мая 2018 - 04:42

Доброго времени суток.

 

Лично я в своей работе использую собственные классы элементов со всеми вытекающими плюсами из этого.

Для этого нужно написать свой декоратор.

 

В классе вертикальной таблицы у меня определяются элементы следующим образом:

 

1) Все строки: 

public virtual IList<IWebElement> Rows => FindElements(By.CssSelector("tbody tr"));
 
2) Все столбцы:
public virtual IList<IWebElement> Columns => FindElements(By.CssSelector("tr:first-child th"));
 
3) Все ячейки:
public virtual IList<IWebElement> Cells => FindElements(By.CssSelector("tbody tr td"));
 
Здесь надо учитывать то, что поиск элементов происходит от родительского.
Так же использую свой фреймворк, где у меня работают следующие атрибуты:
 
[BlockTitle("Модальное окно с заказом")]
[FindBy(XPath = "//div[@class = \'modal-content\']")]
[TimeToSearch("block_waiting_timing")]
    public class OrderModalBlock : ABlock
    {       
 
        [FindBy(XPath = "(.//table[preceding::*[contains(text(), \'Заказ\')]]) [1]", ParentElement = "Модальное окно с заказом")]
        [ElementTitle("Заказ")]
        private AHorizontalTable _orderTable;

      ...../

   }

 

Соответственно, таблица является дочерним элементом блока элементов.

А элементы внутри таблицы - соответственно дочерними элементами самой таблицы.

 

Сам локатор для поиска текста в ячейке такой (точка в начале Xpath означает, что поиск нужно осуществлять внутри родительского элемента - это важно, если у вас на странице не одна таблица): 

var element = FindElement(By.XPath($".//tbody//tr[{rowId}]//td[contains(text(), \'{textInRow}\')]|.//tbody//tr[{rowId}]//td[*[contains(text(), \'{textInRow}\')]]"));
 
Такой xpath либо найдет текст в самой ячейке, либо в любом элементе в этой ячейке. 
В моём примере, ячейка хранит ссылку, т.е. <td><a...></a></td> - и текст нужно было искать именно в элементе <a>.
 
Если вам нужно искать текст не смотря на регистр, и у вас XPath 1.0, то можно воспользоваться вспомогательным методом. 
Например таким:
 
public static string ContainsTextIgnoreCase(string text)
        {
            return
                $"text()[contains(translate(., \'ABCDEFGHIJKLMNOPQRSTUVWXYZЙФЯЦЫЧУВСКАМЕПИНРТГОЬШЛБЩДЮЗЖХЭЪ\', \'abcdefghijklmnopqrstuvwxyzйфяцычувскамепинртгоьшлбщдюзжхэъ\'), \'{text.ToLower()}\')]/parent::*";
        }

  • 1




Яндекс.Метрика
Реклама на портале