Какая должна быть правильная структура тестов?

Подскажите пожалуйста. Как правильно спроектировать автотесты используя Page Factory. В автоматизации я новичок, поэтому строго не судите)
Создал базовый класс TestBase, где описал Before test: объявляю и инициализирую драйвер, пейджы, открваю сайт, инициализирую класс с переменными.
От базового класса наследуюсь класом Tests, где прописываю все свои тесты.
Отдельно в папке создаю пейджы, где все переменные и методы статические.
Отдельно создаю класс с переменными, они все статические.
Класс с переменными импортирую в пейджи там где надо и использую.
Пейджи импортирую в базовый класс.

Вопросы:

  1. Нужно ли инициализировать пейджи и класс с переменными в базовом классе все сразу в Before test?
  2. Кооректно ли обявлять в классах пейджах и переменных статические переменные и методы, или это как то можно по другому обойти?
  3. Корретно ли я наследовал и импортил?

Какие вообще лучшие практики построения автотестов?

image

  1. Было бы легче Вам помочь, если бы Вы вместо описание структуры добавили примеры кода классов.
  2. Даже судя по тому, что у Вас в описании часто упоминается слово “статические”, это сразу будет проблемой в будущем. Избавляйтесь от статики.
  3. Непонятно, что за отдельный класс с переменными? Также не понятно, что означает “Отдельно в папке создаю пейджы”? В какой папке?
  4. Что в Вашем понимании импортировать один класс в другой?

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

1 лайк

Код одного класса есть - отлично. А что в других?
Будет легче если Вы покажите код всех классов.

И того, что увидел:

  1. А что будет если в классе TestBase все поля сделать не статическими?
  2. Отдельного класса Variables быть не должно
  3. Инициализировать все страницы сразу тоже нет необходимости. Создавайте страницу, когда это необходимо.
  4. А за что отвечают PageManager’s?

PageManagers - папка с пейджами менеджеров, это так для систематизации и удобства

Я наверное повторюсь

За что отвечают “пейджи менеджеров” или “менеджеры пейджей”?
“систематизации и удобства” чего?
Что в классе Tests и как выглядит код одного теста?

С Variables стало яснее. Это класс с тестовыми данными. Обычно такую информацию хранят в Properties файлах (Java Properties File: How to Read config.properties Values in Java? • Crunchify)
Строка Variavles = new Variavles(); в классе TestBase не нужна.

import org.openqa.selenium.By;
import org.testng.Assert;
import org.testng.annotations.Test;
import pages.DashBoardPage;
import pages.Variables;

public class Tests extends TestBase{
@Test
public void logIn(){
loginPage.logIn();
Assert.assertTrue(dashBoardPage.getUsersButton().isEnabled());
}

@Test (dependsOnMethods = {“logIn”})
public void createManager() throws InterruptedException {
createManagerPage.createManager();
Assert.assertEquals((driver.findElements(By.cssSelector(“div.manager-info__name > span + br + span”)).get(0).getText()), Variables.getEmailManager());
}
}

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

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

В классе Tests зависимости между тестами быть не должно (dependsOnMethods = {“logIn”}).
Вот это вынесите в класс вашей страницы в какой то метод который будет возвращать текст этого элемента и потом уже дергайте этот метод в тестовом классе.

(driver.findElements(By.cssSelector(“div.manager-info__name > span + br + span”)).get(0).getText())

а как я могу проводить тесты если не залогинюсь? Или мне перед каждим методом логиниться? или есть возможность запустить тест уже залогиненым?

логин в Before делайте

Итак по-порядку. Начнём с тестов.

  1. Идея наследовать все тестовые классы от базового тестового класса, в котором выполняются настройки - это самая стандартная практика, которая в 90% несёт пользу.

  2. В базовом тестовом классе не нужно инициализировать все страницы сразу. По-хорошему в тестовых классах вообще не должно быть инициализации страниц. Этим должны заниматься вспомогательные классы. В идеале инициализированные страницы должны возвращаться из методов. Например, loginPage.logIn() должен возвращать страницу dashBoardPage. Если метод loginPage.logIn() параметризировать и дать возможность передавать данные для логина из теста, то в случае если данные неверны - этот метод должен возвращать LoginPage.

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

  4. Насколько я понял в качестве тестового фреймворка Вы используете TestNG. Внимательно изучите, что означают его аннотации. В TestNG существует следующая базовая иерархия тестовых сущностей. Самая маленькая - тестовый method (метод тестового класса). Тестовые методы объединяются в тестовые классы. Далее следует Test, который может состоять из нескольких тестовых классов или тестовых методов разных тестовых классов. Test-ы собираются в Suite. Перепроверьте чего Вы хотели достигнуть используя @BeforeTest аннотацию. @BeforeTest будет выполнятся раз перед TestNG Test. Не перед каждым методом с аннотацией @Test.

  5. Детали инициализации драйвера лучше убрать из базового тестового класса в какой-нибудь DriverManager или DriverFactory. А в базовом тестовом классе обращаться для получения драйвера к DriverManager или DriverFactory. Также не используйте абсолютные пути для указания пути к файлу драйвера - пользуйтесь относительными путями к папке вашего рабочего проекта.

  6. Implicit wait 10 секунд - это многовато. Пользуйтесь explicit wait-ами где это необходимо.

  7. Что Вы проверяете в тесте logIn()? Насколько я понимаю, Вы хотите проверить, что после логина открывается страница dashBoardPage. Если метод loginPage.logIn() будет возвращать страницу dashBoardPage (как было сказано выше), то Вам нужно проверить, что в результате его выполнения вернётся экземпляр (instanseof) этой странице. Button-ами в тесте лучше не оперировать.

  8. Как уже было сказано в посте Какая должна быть правильная структура тестов? - #10 от пользователя ordeh зависимости между тестами - это крайняя мера и если есть возможность их не делать, то лучше не делать. Понятно, что для второго теста Вам нужно залогиниться, как и скорее всего для третьего понадобиться. Это то, что нужно делать в каждом тесте отдельно либо вынести в базовый тестовый класс с аннотацией @BeforeMethod

  9. Тест throws InterruptedException. Такого как по мне быть не должно. Такие эксепшены должны обрабатываться уровнем ниже.

  10. Как уже было сказано в том же посте от @ordeh все локаторы должны быть в классах страниц.

  11. Рекомендую сразу учиться пользоваться более продвинутыми Assert-ами Hamcrest Tutorial

Теперь к пейджам

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

  2. Драйвера, кстати, в коде тестов быть не должно. По-хорошему драйвер должен быть только в классах страниц.

  3. Старайтесь не использовать тестовые данные (Variables) в коде страниц. Делайте методы страниц параметризируемыми, а данные передавайте из тестов. Например, метод logIn() можно переделать на logIn(String username, string password)

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

  5. Внутри пейджовых методов также следует выполнять ожидание завершения действия/загрузки страницы. В Вашем случае после логина, если драйвер сам не дожидается загрузки страницы (такое бывает если нажатие кнопки не ведёт к переходу на другую страницу в браузере = смене URL), надо дождаться пока загрузится dashBoardPage

Общая рекомендация - избавляйтесь от static-ков по-максимуму.

5 лайков

Огромное спасибо!!!

1 лайк
  1. Как в джавке сделать что бы метод возвращал или логин пейдж или следующую пейджу?
  2. Чем плохо использовать 1 обьект в нескольких тестах которые допустим создаются в бефор сьют?

  1. Чем плох статический драйвер? Как не явно в тесте передать драйвер?
  2. ка ктогда быть если нам нужно остаться на той же странице после появления ошибки?
  3. Так всё же чем плох статик?

Если это вопросы, то попробую частично ответить.
2. Сделать метод не void а чтобы возвращал новый PageObject. Например, есть Login метод, который логинится. В методе сделать все действия, а в конце - return new MainPage(driver) - код может меняться, в зависимости от того, как устроен framework.

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

По пункту 2 добавлю еще пример на селениде

public LoginPageImpl openLoginPage(){
        btnOk.click();
        return page(LoginPageImpl.class);
    }

@Valentin_G, @ordeh Как вернуть объект я и так знаю, я имею в виду что если нам после нажатия на кнопку логин не всегда нужно переходить на новую страницу? Например после нажатия должно появится ерор что логин не правильный. Это возможно только явной передачей типа возвращаемого объекта при возвращаемом дженерик типе, ну или городить костыли с интерфейсами.

А что если я скажу что можно паралелить на уровне CI а не стандартным parallel=true, или использовать потокобезопасные контейнеры для хранения драйверов ну или самый банальный ThreadLocal ?

1 лайк

посмотрите примеры - выберите то что понравится
серебряной пули нет

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

Остерегайтесь оверинжиниренга
https://comaqa.by/2018/07/27/comaqa-spring-2018-ui-automation-antipatterns/

2 лайка

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

По поводу параллелизации на уровне СИ или потокобезопасные контейнеры - про это не могу сказать ничего, никогда не использовал.

1 лайк