Всем доброго времени суток. Я начинающий автоматизатор, поэтому прошу не судить строго и помочь разобраться в своем же коде . Пишу свой pet automation project Selenium+Maven+TestNG+Intellij, использую PageObject и PageFactory. У меня есть пару вопросов.
Есть класс BrowserFactory с приватным конструктором и методом, возвращающим драйвер, - как альтернативу использованию синглтона (его пока не трогаю, оставляю как есть). В проекте использую properties-file. По поводу конструкции if-else – знаю, что смотрится коряво и лучше сделать через switch или enum – со временем переделаю.
public class BrowserFactory {
private BrowserFactory() {
}
public static WebDriver getDriver(String browser){
WebDriver defaultDriver = new FirefoxDriver();
if ("CHROME".equalsIgnoreCase(browser)){
return new ChromeDriver();
} else if ("IE".equalsIgnoreCase(browser)){
return new InternetExplorerDriver();
} else if ("OPERA".equalsIgnoreCase(browser)){
return new OperaDriver();
} else {
return defaultDriver;
}
}
}
Есть класс BrowserHelper, в котором инициализируется необходимый браузер через системную переменную, содержащий собственно реализацию старта и зачистки драйвера.
public class BrowserHelper {
private static WebDriver driver;
private static WebDriverWait wait;
public static void startBrowser(){
driver = BrowserFactory.getDriver("browser");
wait = new WebDriverWait(driver, 30);
driver.get("http://www.somesite.com");
driver.manage().window().maximize();
}
//other code
}
Есть базовый абстрактный класс Page, от которого экстендятся все остальные классы-страницы. В конструкторе этого базового класса получаем объект драйвера из BrowserFactory, делаем вейт и инициализируем PageFactory
public abstract class Page {
WebDriver driver;
WebDriverWait wait;
public Page(){
driver = BrowserFactory.getDriver(System.getProperty("browser"));
wait = new WebDriverWait(driver, 30);//
PageFactory.initElements(driver, this);//
}
Вопросы:
У меня дублируется wait. Где все-таки нужно его оставить, а где убрать?
Нужно ли в каждом наследнике Page объявлять свой драйвер и вейт? И соответственно в Page поля драйвер и вейт должны быть публичными (если они таки наследуются в детях и в детях мы их не объявляем) или все-таки приватными (если в каждом наследнике мы должны объявлять и инициализировать их отдельно)? Понимаю что здесь лежит весь корень зла - так как тесты падают с NullPointerException.
Касательно PageFactory: достаточно ли в наследниках Page просто вызова конструктора родителя через super() или все-таки нужно инициализировать PageFactory на каждой странице?
public LoginPage() {
super();
}
спасибо огромное Богдан за план фреймворка, но боюсь я такую конструкцию еще не осилю. С пейджами и тестами все понятно. В Controls выносятся все вебэлементы? а что в UIMap выносится?
в абстрактном. Но правильнее будет сказать, что его не должно быть в хелперах, пейджах, тестах. Следовательно у каждых таких должен быть свой абстрактный класс: BaseHelper, BaseTest, BasePage, я когда-то делал именно так. Но сам драйвер и тип драйвера у меня был реализован в других классах. DriverSetup, DriverType. Затем можно сделать getDriver (синглтоном, к примеру) и уже в абстрактных классах подключать его как-то так:
public abstract class BaseTest {
@BeforeClass
public void setUp() {
driver = DriverSetup.getInstance().getDriver();
}
тоже самое можно сделать в абстрактном классе Хелпера и унаследовавшись от него использовать драйвер… Делал когда-то так.
всем спасибо что откликнулись на мою просьбу .
Меня очень интересует где мне все-таки делать инициализацию веб элементов с помощью PageFactory (я использую аанотацию @FindBy) - на каждой Page или достаточно в абстрактном родителе (как у меня сейчас и реализовано). NullPointerException может выдавать именно он
Насколько мне известно родитель будет инициализировать только свои элементы, а не элементы дочернего класса, поэтому у вас будет NPE. Два варианта - либо инициализировать в конструкторе каждого PageObject либо в методе init этого объекта, который будете вызывать вручную. Инициализация само собой ленивая, через PageFactory.
this в базовом классе всегда будет ссылаться на вызвавшего наследника. Инициализация в стандартной фабрике проходит итеративно, пробегаясь по всей цепи наследования.
Чтобы понять причину NPE, нужно для начала увидеть stacktrace, а не собирать вокруг себя толпу людей для гадания на кофейной гуще.