Проблема:
Многих людей, хлебом не корми – дай только пописать лишний код, да и передать лишний вебдрайвер каждому ПейджОбжекту в самый конструктор…
В примере ниже, я покажу, как избежать лишних явных созданий экземпляра вебдрайвера и лишних инициализаций PageFactory.InitElements
Я понимаю, что многие начали работу с PageObject по этому примеру с PageFactory, но ведь это совсем не значит, что этот пример самый оптимальный. Это – просто пример.
Я не хочу каждый раз инициализировать вебдрайвер. Я не хочу, каждый раз инициализировать страницу при помощи PageFactory. Я просто, хочу писать код…
Решение
Для начала, разберемся с надоедливым созданием нового экземпляра WebDriver в каждом тесте. Пусть Вебдрайвер – сам себя создает, когда мне это нужно.
Вызов Browser.Driver(), вернет либо уже созданный Вебдрайвер, либо создаст его при первом обращении.
// Вызови Browser.Driver(), и драйвер – твой!
public static class Browser
{
private static IWebDriver driver;
// Автоматически создает новый WebDriver при
// первом обращении, либо возвращает уже созданный
public static IWebDriver Driver()
{
// Если driver равен null (??), то создать новый FirefoxDriver
driver = driver ?? new FirefoxDriver();
return driver;
}
// Driver капут
public static void CloseDriver()
{
if (driver != null) driver.Quit();
}
// Эммм… у статических классов – нет деструктора.
// Тут создается объект обычного класса Finalizer,
// который будет безжалостно уничтожен .NET фреймворком
// по завершению теста. А за собой он потянет закрытие
// вебдрайвера.
static readonly Finalizer finalizer = new Finalizer();
sealed class Finalizer
{
~Finalizer()
{
CloseDriver();
}
}
}
Идем дальше.
Я хочу, чтобы каждая страница сама себя инициализировала при помощи PageFactory.InitElements. Тем самым, при создании нового экземпляра страницы, будет возвращаться готовая страница, с которой уже можно работать, а не какой-то полуфабрикат, который еще и на фабрику отправлять нужно.
Для этого, необходимо создать специальный базовый класс.
В .NET есть такая особенность: конструкторы без параметров дочерних классов, будут автоматически вызывать, в первую очередь, конструктор базового класса.
Вы спрашиваете, будет ли это работать и в Java? – Не знаю, попробуйте.
// Это базовый класс для всех страниц
public abstract class BasePage
{
// Полезное свойство. Позволяет писать меньше точек.
public IWebDriver Driver { get { return Browser.Driver(); } }
// Конструктор без параметров дочернего класса,
// автоматически вызывает конструктор без параметров базового.
// Тут очень важно, что это работает только для конструкторов
// ** без параметров **.
// На этом свойстве и сыграем.
public BasePage()
{
// На самом деле, this – это будет объект дочернего класса.
// PageFactory умеет с ним работать со столь запутанной
// схемой.
// Убийца – садовник. Извините.
PageFactory.InitElements(Driver, this);
}
}
Пришел черед создать PageObject, который декларирует страницу. В нем есть два действия (метода):
- Invoke() – прото открывает страницу
- Calcualte() – выполняет действия по вычислению логарифма
public class LogCalculatorPage : BasePage
{
// =================== Элементы страницы =======================
//
[FindsBy(How = How.XPath, Using = @"//input[@name='b']")]
protected IWebElement txtLogBase { get; set; }
[FindsBy(How = How.XPath, Using = @"//input[@name='x']")]
protected IWebElement txtLog { get; set; }
// Товарищи, эти локаторы были записаны на скорую руку при помощи
// SWD Page Recorder
// И мне лень было их оптимизировать. Но, это не значит, что это
// правильный путь.
[FindsBy(How = How.XPath, Using = @"//form[@name='calcform1']/table[1]/tbody[1]/tr[5]/td[2]/input[1]")]
protected IWebElement btnCalculate { get; set; }
[FindsBy(How = How.XPath, Using = @"//input[@name='y']")]
protected IWebElement txtResult { get; set; }
// =================== ~~~~~~~~~~~~~~~~~ =======================
// Тыщ-тыщ, «вызывает» страницу
public void Invoke()
{
// Я еще раз напомню, что Driver – унаследован из базового
// класса,
// А на самом деле, при каждом таком обращении, вызывается
// Browser.Driver().
// Но, удобно же, правда?
Driver.Navigate().GoToUrl(@"http://www.rapidtables.com/calc/math/Log_Calculator.htm");
}
// Калькъ!
public double Calcualte(string logBase, double logValue)
{
txtLogBase.SendKeys(logBase);
txtLog.SendKeys(logValue.ToString());
btnCalculate.Click();
var rawResult = txtResult.GetAttribute("value");
return Convert.ToDouble(rawResult);
}
}
А вот так выглядит тест:
static void Main(string[] args)
{
// Магия базового класса! При создании объекта,
// он уже будет проинициализирован PageFactory
var calcPage = new LogCalculatorPage();
calcPage.Invoke();
var result = calcPage.Calcualte("10", 100);
Console.WriteLine("Вот такой суровый логарифм: " + result);
}
Видео работы теста, чтобы вы не говорили, что я обманываю