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

Публикации Noksa

117 публикаций создано Noksa (учитываются публикации только с 03 мая 2023)



#166574 Как ускорить автотесты, давайте соберем все возможные варианты

Отправлено автор: Noksa 12 июня 2018 - 09:57 в Selenium - Functional Testing

У ТС нет цели уменьшить количество авто-тестов.

У него совсем другая цель.

 

 

 

Истинное искусство тестировщика - уменьшение числа тестов.

Предлагаете уменьшать покрытие? 

 

При релизах всё равно надо гонять все тесты. И чем их больше (т.е. больше покрытие) - тем лучше.

 

 

 

Это прошлый век. Сейчас выгоднее на время прогона тестов арендовать вычислительные мощности в облаке.

 

Кому как. Я за стабильную мощную физическую машину.

 

И ещё учитывайте, что не все компании, а если быть точнее - их политика безопасности, разрешает использовать какое-либо облако.




#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 - честно говоря никогда не думал об этом.

 

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




#166550 Как ускорить автотесты, давайте соберем все возможные варианты

Отправлено автор: Noksa 10 июня 2018 - 20:05 в Selenium - Functional Testing

Самое банальное и очевидное - улучшить железо, а как следствие количество потоков.



#166541 Selenium-web-driver ----- ошибка Returned node (null) was not a DOM el

Отправлено автор: Noksa 08 июня 2018 - 17:06 в Selenium - Functional Testing

Пример кода, где ошибка валится?



#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());
        }



#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()

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




#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, который содержит в себе вызовы мелких методов, а не дублирует их логику.

 

 

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




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

Отправлено автор: Noksa 07 июня 2018 - 16:43 в Автоматизированное тестирование

Наследованию всегда надо предпочитать композицию, если есть возможность.
И вообще, PageObject в помощь.



#166457 getText (getValue) для дефолтного значения dropbox

Отправлено автор: Noksa 04 июня 2018 - 07:51 в Selenium - Functional Testing

Надо учитывать, что если значение никакое не выбрано - у вас вылетит эксепшн. 




#166436 Скачивание файлов селениумом через headless режим C#

Отправлено автор: Noksa 01 июня 2018 - 10:24 в Selenium - Functional Testing

 

 

 

Что же в этом костыльного? :) 

если файл генерится в бэк-энде, то там его и можно проверить интеграционными тестами например

 

 

Да, но если он не генерится по нажатию кнопки при этом, что в этом случае сказать? "Сорри, я проверял только бизнес-логику"? 

 

Нужен именно вариант с интерфейсом, иначе я бы не парился :)

Сейчас я просто убрал headless режим, но без него хром явно медленнее соображает.




#166432 Скачивание файлов селениумом через headless режим C#

Отправлено автор: Noksa 01 июня 2018 - 08:26 в Selenium - Functional Testing

Я не спрашивал, нужен этот тест или не нужен.

 

У меня тест - имитация действий пользователя.

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

 

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

 

Что же в этом костыльного? :) 




#166427 Скачивание файлов селениумом через headless режим C#

Отправлено автор: Noksa 01 июня 2018 - 06:03 в Selenium - Functional Testing

Коллеги, доброго времени суток.

 

Есть метод, который выполняет следующее:


1) Лочит блок кода, в котором происходит:
а) Запоминание количества файлов в директории
б) Клик по кнопке, в результате которого начинается скачивание файла и ожидание, пока файл скачается целиком
в) Сравнение двух коллекций - до скачивания и после - получаем разницу в 1 файл, дёргаем искомый файл.
2) Выходим из лока и работаем дальше с этим файлом.

 

Всё работает хорошо, пока не установить запуск хрома в режиме headless: chromeOptions.AddArgument("--headless");

Я нашёл в репо селениума открытый баг https://github.com/S...ium/issues/5159 в котором пока судя по всему нет решения проблемы.

 

Есть ли какие-то костыльные workaround, как скачать файл в этом режиме сейчас?

 

Версии драйвера, браузера последние.




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

Отправлено автор: Noksa 31 мая 2018 - 07:21 в Selenium - Functional Testing

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




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

Отправлено автор: Noksa 31 мая 2018 - 07:16 в Selenium - Functional Testing

Ещё такой вопрос:

Заранее определяю элемент: By checkboxes = By.XPath("//input[@name='CurrentObject.Show']");

Потом использую его в коде: driver.FindElement(checkboxes).Click();

 

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

И внутри цикла получается только так: driver.FindElement(By.XPath("(//input[@name='CurrentObject.Show'])" + "[" + n1 + "]")).Click();

 

Можно как-нибудь сделать чтобы это было похоже на : driver.FindElement(checkboxes[n1]).Click(); как-то вот так???

 

Вы можете использовать метод FindElements.

 

Например так: driver.FindElements(checkboxes).

 

Он вернёт вам список всех найденных элементов по переданному локатору.

А потом уже можете в foreach пройтись по всем элементам из списка:

 

foreach (var checkBox in checkboxes)

                {
                    checkBox.Click();
                }
 
Если метод только один, то фигурные скобки можно опустить:

foreach (var checkBox in checkboxes) checkBox.Click();                

 
Либо через лямбду:
checkboxes.ToList().ForEach(_ => _.Click());



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

Отправлено автор: Noksa 31 мая 2018 - 04:42 в Selenium - Functional Testing

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

 

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

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

 

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

 

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::*";
        }



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

Отправлено автор: Noksa 30 мая 2018 - 20:13 в Selenium - Functional Testing

Не получалось составить корректный xpath/css селектор!

Пока ждал ответа не сидел сложа руки и внутри цикла написал такое:

t = driver.FindElement(By.XPath("//tr[" + N + "]/td/table/tbody/tr/td")).Text;
N++;

Работает, но я не уверен насколько надёжна такая конструкция и может быть есть вариант проще или корректнее?

Из вашего локатора надо убрать лишнее до //table. Да и в целом переписать его.
К тому же, если в вашей таблице в ячейке будет какой-то элемент с текстом, а не просто текст - локатор не сработает.
Например ссылка с текстом.
Здесь нужно использовать ИЛИ.
Завтра как доберусь к компьютеру могу подробнее написать правильный универсальный локатор.



#166301 Использовать созданный ранее объект WebDriver

Отправлено автор: Noksa 23 мая 2018 - 05:31 в Selenium - Functional Testing

 

Можно ли использовать уже существующий объект Selenium WebDriver? Я имею в виду следующее: в первом тесте я создаю этот объект, а по его окончанию не закрываю и не уничтожаю. После окончания теста процесс продолжает оставаться в памяти. Через какое-то время у меня возникает потребность запустить другой тест, в котором используется тот же URL. Могу ли я каким-то образом в этом новом тесте использовать старый объект драйвера, активировать существующее окно браузера и выполнить в нем новый тест?

не закрывайте например :) или статический вебдрайвер используйте но если статик будет тогда не будет и паралельных тестов

 

 

Достаточно положить статик драйвер в ThreadLocal<>, чтобы были параллельные тесты по потокам.