Как инициализировать SelectElements при использовании PageFactory/FindsBy в Selenium C#?

Я создаю объектную модель страницы в Selenium WebDriver для С#, используя класс PageFactory.

К сожалению, я обнаружил, что FindsByAttribute не инициализирует SelectElement (тег HTML <select>/выпадающее меню). До сих пор я встречал или придумал несколько идей, чтобы обойти это, но ни одна из них не идеальна:

  1. PageFactory и FindsByAttribute - это sealed, поэтому я не могу заставить это, просто унаследовав их.
  2. Ручное создание экземпляра SelectElement из IWebElement в каждом методе довольно беспорядочно и дублирует. Он также игнорирует очевидное встроенное ожидание в PageFactory и выдает NoSuchElementExceptions, если только я не добавлю ожидание каждый раз, когда делаю это, что потребовало бы повторения локатора повсюду, нарушая (частично) цель POM.
  3. Обертывание каждого свойства IWebElement свойством SelectElement менее запутанно, но по-прежнему имеет ту же проблему ожидания, что и выше.

На данный момент лучшим вариантом является № 3 и написание оболочки для SelectElement, которая просто добавляет ожидание к каждому методу. Хотя это решение будет работать, оно значительно увеличит объем кода каждой страницы, так как вместо этого (гипотетического) красивого кода:

[FindsBy(How = How.Id, Using = "MonthDropdown")]
public SelectElement MonthDropdown;

Я застрял с оберткой-оболочкой (чего я бы предпочел избежать) и:

[FindsBy(How = How.Id, Using = "MonthDropdown")]
private IWebElement _monthDropdown;
public Selector MonthDropdown
{
    get { return new Selector(MonthDropdown, Wait); }
}

Поскольку Selector является оболочкой SelectElement, он также должен принимать IWait<IWebDriver>, чтобы он мог ждать, и создавать новый экземпляр Selector каждый раз, когда я к нему обращаюсь.

Есть ли лучший способ сделать это?

РЕДАКТИРОВАТЬ: Сонно вставил неправильные модификаторы доступа. Исправлено. Спасибо, @JimEvans.


person MartinRosenberg    schedule 24.07.2015    source источник
comment
Большое спасибо за этот вопрос и ответ. +1   -  person Happy Bird    schedule 19.06.2017


Ответы (1)


Во-первых, в реализации .NET PageFactory нет «встроенного ожидания». Вы можете легко указать его в вызове InitElements (подробнее об этом чуть позже). В настоящее время лучшим вариантом для вас будет ваш вариант 3, хотя я бы не стал раскрывать члена IWebElement; Я бы сделал его private, так как PageFactory может перечислять частные члены так же легко, как и общедоступные. Таким образом, ваш объект страницы будет выглядеть так:

[FindsBy(How = How.Id, Using = "MonthDropdown")]
private IWebElement dropDown;
public SelectElement MonthDropdownElement
{
    get { return new SelectElement(dropdown); }
}

Как вы получаете настоящий IWebElement, когда он вам нужен? Поскольку SelectElement реализует IWrappedElement, вы можете просто вызвать свойство WrappedElement, если вам нужен доступ к методам и свойствам элемента, предоставляемым интерфейсом IWebElement.

Последние версии привязок .NET реструктурировали PageFactory, чтобы сделать его более расширяемым. Чтобы добавить желаемое «встроенное ожидание», вы можете сделать следующее:

// Assumes you have a page object of type MyPage.
// Note the default timeout for RetryingElementLocator is
// 5 seconds, if unspecified.
// The generic version of this code looks like this:
// MyPage page = PageFactory.InitElements<MyPage>(new RetryingElementLocator(driver), TimeSpan.FromSeconds(10));
MyPage page = new MyPage();
PageFactory.InitElements(page, new RetryingElementLocator(driver, TimeSpan.FromSeconds(10))); 

Кроме того, если вам действительно нужно настроить работу вещей, вы всегда можете реализовать IPageObjectMemberDecorator, который позволяет полностью настроить порядок перечисления атрибутов и установки значений для свойств или полей, украшенных этими атрибутами. . Одна из (неуниверсальных) перегрузок PageFactory.InitElements принимает экземпляр объекта, реализующего IPageObjectMemberDecorator.

Я оставлю в стороне тот факт, что правильные реализации шаблона объектов страницы, как строго определенные, не должны выставлять какие-либо объекты WebDriver за пределами каждого объекта страницы. В противном случае все, что вы реализуете, — это «оболочка страницы», что является вполне допустимым подходом, но не то, что можно было бы назвать «объектом страницы».

person JimEvans    schedule 24.07.2015
comment
Всегда нравится твой ответ. Что-то новое, чтобы узнать. Однако в чем логика RetryingElementLocator? С точки зрения страниц с большим количеством Ajax или Angular было бы полезно найти элемент? - person Saifur; 24.07.2015
comment
Странно то, что, хотя я не проверял код PageFactory на наличие ожидания, я не ожидал, что оно будет, и тем не менее он все еще ожидает загрузки элементов. Я знаю, что строгий шаблон Page Object не раскрывает элементы. Они выставлены только сейчас, потому что я довольно рано нахожусь в процессе создания POM и нахожу это временно удобным. - person MartinRosenberg; 24.07.2015
comment
Спасибо! RetryingElementLocator избавил меня от необходимости заворачивать SelectElement. Я надеялся, что есть способ получить что-то более чистое (например, FindsBy непосредственно на SelectElement), но меня не удивляет, что его нет. Не то, чтобы мне это обязательно нужно прямо сейчас, но не могли бы вы рассказать больше об использовании IPageObjectMemberDecorator? Даже у Google есть только 10 результатов для этого. - person MartinRosenberg; 24.07.2015