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

Selenium WebDriver: полное руководство
онлайн, начало 22 декабря
Программирование на Java для тестировщиков
онлайн, начало 22 декабря
Программирование на Python для тестировщиков
онлайн, начало 15 декабря
Тестирование защищенности веб-приложений
онлайн, начало 15 декабря
Фотография

Как вынести создание объекта из теста

PageObject Selenium Java

  • Авторизуйтесь для ответа в теме
Сообщений в теме: 9

#1 sanchos0210

sanchos0210

    Новый участник

  • Members
  • Pip
  • 6 сообщений

Отправлено 07 Декабрь 2017 - 12:45

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

Я недавно начал изучать автоматизированное тестирование с помощью selenium; пишу на Java. В качестве тестового фреймворка использую TestNG.

Также недавно познакомился с таким паттерном, как PageObject и на основе этого паттерна проектирую архитектуру автотестов. В данный момент имею следующую архитектуру: классы с тестами, которые наследуются от базового тестового класса (BaseTest); отдельно лежат классы с пэйджами, которые наследуются от базового пейджа (BasePage) и в самом вверху лежит абстрактный класс API, который наследуют два класса: BaseTest и BasePage. В этом классе API лежат хелперы и константы, которые я использую и в пейджах и в тестах. Вот в крации об архитектуре.

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

Пример: допустим есть тестовый класс LoginTests в котором лежит один тестовый метод loginWithInvalidEmail(). в этом тестовом методе я использую объект класса LoginPage. Я хочу это создание объекта вынести из метода в тестовый класс, вот как в в моем коде ниже: 

 

public class LoginTests extends BaseTest {

 

LoginPage loginPage = new LoginPage(driver);

@Test
public void loginWithInvalidEmail() {
open(TESTPROLINE);
loginPage.emailInput.sendKeys(invalidEmail);
loginPage.passwordInput.sendKeys(PASSWORD);
loginPage.submitButton.click();
assertThat(textToBePresentInElement((loginPage.errorMessage), "Email or password is incorrect!"));
}

 

Но проблема в том, что когда я запускаю этот тест, то происходит следующее: открывается браузер, он настраивается(размер на полный экран, задаются неявные ожидания) и после этого загружается моя страница TESTPROLINE и когда дело доходит до строчки "loginPage.emailInput.sendKeys(invalidEmail);", то тест падает с ошибкой java.lang.NullPointerException. я так понимаю тут проблема в базовом тестовом классе, в котором происходит та самая "настройка браузера", поэтому привожу также код из класса BaseTest:

public class BaseTest extends API {

public static WebDriver driver ;

@BeforeMethod
public void initializeDriver() {
System.setProperty("webdriver.chrome.driver", "C:\\Program Files\\chromedriver_win32\\chromedriver.exe");
driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.manage().window().maximize();
}

@AfterMethod
public void tearDown() {
// Close all browser windows and safely end the session
driver.quit();
}

}

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


  • 0

#2 BabyRoot

BabyRoot

    Специалист

  • Members
  • PipPipPipPipPip
  • 549 сообщений


Отправлено 07 Декабрь 2017 - 12:49

А покажите свой ЛогинПэйдж.


  • 0

#3 sanchos0210

sanchos0210

    Новый участник

  • Members
  • Pip
  • 6 сообщений

Отправлено 07 Декабрь 2017 - 12:53

/


  • 0

#4 Lzk

Lzk

    Опытный участник

  • Members
  • PipPipPipPip
  • 479 сообщений
  • ФИО:Олег
  • Город:Мск

Отправлено 07 Декабрь 2017 - 12:55

Инициализацию обьекта можно вынести в Before.

А чем тебя не устраивает создание обьекта страницы в тесте ? 

Я бы спрятал инит драйвером бы вот как раз.


  • 0

#5 sanchos0210

sanchos0210

    Новый участник

  • Members
  • Pip
  • 6 сообщений

Отправлено 07 Декабрь 2017 - 12:56

А покажите свой ЛогинПэйдж.

public class LoginPage extends BasePage {

public LoginPage(WebDriver driver) {
super(driver);
}

@FindBy(id "inputEmail3")
public WebElement emailInput;

@FindBy(id "inputPassword3")
public WebElement passwordInput;

@FindBy(id "login_btn")
public WebElement submitButton;

}


  • 0

#6 sanchos0210

sanchos0210

    Новый участник

  • Members
  • Pip
  • 6 сообщений

Отправлено 07 Декабрь 2017 - 13:00

Инициализацию обьекта можно вынести в Before.

А чем тебя не устраивает создание обьекта страницы в тесте ? 

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


  • 0

#7 BabyRoot

BabyRoot

    Специалист

  • Members
  • PipPipPipPipPip
  • 549 сообщений


Отправлено 07 Декабрь 2017 - 13:13

Такую страницу надо инициализировать через пейджФактори.

https://github.com/S...iki/PageFactory

так не правильно:

LoginPage loginPage = new LoginPage(driver);


  • 0

#8 sanchos0210

sanchos0210

    Новый участник

  • Members
  • Pip
  • 6 сообщений

Отправлено 07 Декабрь 2017 - 13:23

Такую страницу надо инициализировать через пейджФактори.

https://github.com/S...iki/PageFactory

так не правильно:

LoginPage loginPage = new LoginPage(driver);

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

public LoginPage(WebDriver driver) {
super(driver);
}

а вот и инициализация страницы в родительском классе: 

 

public class BasePage extends API {

private WebDriver driver;

public BasePage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}

}

 

Тоесть если я создание объекта засуну в тестовый метод. То все будет работать.


  • 0

#9 TatyanaV

TatyanaV

    Постоянный участник

  • Members
  • PipPipPip
  • 207 сообщений
  • ФИО:Воробьева Татьяна


Отправлено 08 Декабрь 2017 - 11:28

Я предполагаю, что в тот момент, когда у вас инициализируется страница - driver == null.

 

ну на самом деле у меня в классе LoginTests не один тест, а несколько.

 

Если не планируете реализовывать PageManager - делаете в классе LoginTests метод:

private LoginPage loginPage() {
    if (loginPage == null) {
        loginPage = new LoginPage(driver);
    }
}

В методах-тестах используете уже не элемент loginPage, а метод loginPage().

Если элемент уже будет создан - все ок, будет использован он. Если его вдруг ещё не будет - он будет создан и инициализирован.

 

Но у Вас есть другая проблема.

Вы (в данный момент) инициализируете страницу при создании класса с тестами. Один раз.

А вот драйвер у Вас пересоздается заново перед каждым методом (стоит аннотация BeforeMethod, а не BeforeClass). Т.е. теоретически - и при Вашем текущем варианте, и при использовании предложенного выше метода - объект страницы уже на втором тесте будет ссылаться на несуществующий driver. В данном случае я имею ввиду, что из-за BeforeMethod "предыдущий" браузер будет закрыт после предыдущего теста, а для этого теста - будет открыт уже новый браузер, о котором объект страницы и знать не знает (он продолжает ссылаться на "старый" driver).

 

Тут два варианта:

1. все ваши тесты в этом классе НЕ требуют запуска нового браузера (м.б. нужен просто переход на "главную" страницу). Тогда лучше изменить аннотацию - браузер будет инициализироваться один раз перед запуском первого теста этого класса. Главное не забыть тогда и у tearDown аннотацию сменить.

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

 

Вообще - мне кажется более оптимальным вариантом использование PageManager.

Драйвер создается один раз (BeforeClass) и передается в конструктор PageManager.

PageManager рулит страницами, создаёт их, когда нужно - инициализирует имеющимся у него драйвером.

Тесты ничего не создают и не инициализируют - только используют страницы, запрошенные у PageManager (как он их создаёт / инициализирует и т.д. - сами тесты волновать не должно, т.к. это не их задача).


  • 0

#10 sanchos0210

sanchos0210

    Новый участник

  • Members
  • Pip
  • 6 сообщений

Отправлено 12 Декабрь 2017 - 10:34

Я предполагаю, что в тот момент, когда у вас инициализируется страница - driver == null.

 

ну на самом деле у меня в классе LoginTests не один тест, а несколько.

 

Если не планируете реализовывать PageManager - делаете в классе LoginTests метод:

private LoginPage loginPage() {
    if (loginPage == null) {
        loginPage = new LoginPage(driver);
    }
}

В методах-тестах используете уже не элемент loginPage, а метод loginPage().

Если элемент уже будет создан - все ок, будет использован он. Если его вдруг ещё не будет - он будет создан и инициализирован.

 

Но у Вас есть другая проблема.

Вы (в данный момент) инициализируете страницу при создании класса с тестами. Один раз.

А вот драйвер у Вас пересоздается заново перед каждым методом (стоит аннотация BeforeMethod, а не BeforeClass). Т.е. теоретически - и при Вашем текущем варианте, и при использовании предложенного выше метода - объект страницы уже на втором тесте будет ссылаться на несуществующий driver. В данном случае я имею ввиду, что из-за BeforeMethod "предыдущий" браузер будет закрыт после предыдущего теста, а для этого теста - будет открыт уже новый браузер, о котором объект страницы и знать не знает (он продолжает ссылаться на "старый" driver).

 

Тут два варианта:

1. все ваши тесты в этом классе НЕ требуют запуска нового браузера (м.б. нужен просто переход на "главную" страницу). Тогда лучше изменить аннотацию - браузер будет инициализироваться один раз перед запуском первого теста этого класса. Главное не забыть тогда и у tearDown аннотацию сменить.

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

 

Вообще - мне кажется более оптимальным вариантом использование PageManager.

Драйвер создается один раз (BeforeClass) и передается в конструктор PageManager.

PageManager рулит страницами, создаёт их, когда нужно - инициализирует имеющимся у него драйвером.

Тесты ничего не создают и не инициализируют - только используют страницы, запрошенные у PageManager (как он их создаёт / инициализирует и т.д. - сами тесты волновать не должно, т.к. это не их задача).

Спасибо большое за совет с PageManager. Раньше про такое не слышал и поэтому чуть позже обязательно попробую. Сейчас решил проблему следующим образом:

Мои методы создания и закрытия драйвера с аннотациями @BeforeMethod и @AfterMethod находятся в родительском классе BaseTest (как я и писал в самом начале). А в классах с тестами я создал дополнительно метод также с аннотацией @BeforeMethod и запихнул туда инициализацию объектов, которые встречаются в тестах данного тестового класса и получается следующая картина: я запускаю тест, сначала отрабатывает метод из родительского класса, в которых создает драйвер, потом отрабатывает метод непосредственно из тестового класса, который создает нужные мне объекты, а потом уже отрабатывает сам тест, после чего метод из родительского класса закрывает браузер. Вот собственно и всё решение. 


  • 0


Selenium 2.0: стартовый уровень
онлайн, начало 19 января
Программирование на Java для тестировщиков
онлайн, начало 22 декабря
Автоматизация функционального тестирования
онлайн, начало 12 января
Selenium WebDriver: полное руководство
онлайн, начало 22 декабря




Темы с аналогичным тегами PageObject, Selenium, Java

Количество пользователей, читающих эту тему: 0

0 пользователей, 0 гостей, 0 анонимных

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