在 Selenium C# 中使用 PageFactory / FindsBy 时如何初始化 SelectElements?
How to initialize SelectElements while using PageFactory / FindsBy in Selenium C#?
我正在使用 PageFactory 在用于 C# 的 Selenium WebDriver 中构建页面对象模型。
不幸的是,我发现 FindsByAttribute
不会初始化 SelectElement
(HTML <select>
标签/下拉菜单)。到目前为止,我偶然发现或想出了一些解决方法,但其中 none 是理想的:
PageFactory
和 FindsByAttribute
是 sealed
,所以我不能通过继承它们来强制它。
- 在每个方法中从
IWebElement
手动实例化 SelectElement
是相当混乱和重复的。它还会忽略 PageFactory
中明显的内置等待并抛出 NoSuchElementException
s 除非我每次执行此操作时都添加等待 - 这将需要在整个地方重复定位器,击败(部分) POM 的用途。
- 用
SelectElement
属性 包装每个 IWebElement
属性 不那么混乱,但仍然有与上面相同的等待问题。
到目前为止最好的选择是#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。
首先,.NET PageFactory
实现中没有 "built-in wait"。您可以在 InitElements
的调用中轻松指定一个(稍后会详细介绍)。目前,对您来说最好的选择是选项 3,但我不会公开 IWebElement
成员;我将其设置为 private
,因为 PageFactory
可以像 public 一样轻松枚举私有成员。所以你的页面对象看起来像这样:
[FindsBy(How = How.Id, Using = "MonthDropdown")]
private IWebElement dropDown;
public SelectElement MonthDropdownElement
{
get { return new SelectElement(dropdown); }
}
如何在需要时获得实际的 IWebElement
?由于 SelectElement
实现了 IWrappedElement
,如果您需要访问 IWebElement
接口提供的元素的方法和属性,您可以简单地调用 WrappedElement
属性。
最新版本的 .NET 绑定已重组 PageFactory
以使其更具可扩展性。要添加您想要的 "built-in wait",您可以执行以下操作:
// 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 对象。否则,您要实施的只是 "page wrapper," 这是一种完全有效的方法,而不是所谓的 "page object."
我正在使用 PageFactory 在用于 C# 的 Selenium WebDriver 中构建页面对象模型。
不幸的是,我发现 FindsByAttribute
不会初始化 SelectElement
(HTML <select>
标签/下拉菜单)。到目前为止,我偶然发现或想出了一些解决方法,但其中 none 是理想的:
PageFactory
和FindsByAttribute
是sealed
,所以我不能通过继承它们来强制它。- 在每个方法中从
IWebElement
手动实例化SelectElement
是相当混乱和重复的。它还会忽略PageFactory
中明显的内置等待并抛出NoSuchElementException
s 除非我每次执行此操作时都添加等待 - 这将需要在整个地方重复定位器,击败(部分) POM 的用途。 - 用
SelectElement
属性 包装每个IWebElement
属性 不那么混乱,但仍然有与上面相同的等待问题。
到目前为止最好的选择是#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。
首先,.NET PageFactory
实现中没有 "built-in wait"。您可以在 InitElements
的调用中轻松指定一个(稍后会详细介绍)。目前,对您来说最好的选择是选项 3,但我不会公开 IWebElement
成员;我将其设置为 private
,因为 PageFactory
可以像 public 一样轻松枚举私有成员。所以你的页面对象看起来像这样:
[FindsBy(How = How.Id, Using = "MonthDropdown")]
private IWebElement dropDown;
public SelectElement MonthDropdownElement
{
get { return new SelectElement(dropdown); }
}
如何在需要时获得实际的 IWebElement
?由于 SelectElement
实现了 IWrappedElement
,如果您需要访问 IWebElement
接口提供的元素的方法和属性,您可以简单地调用 WrappedElement
属性。
最新版本的 .NET 绑定已重组 PageFactory
以使其更具可扩展性。要添加您想要的 "built-in wait",您可以执行以下操作:
// 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 对象。否则,您要实施的只是 "page wrapper," 这是一种完全有效的方法,而不是所谓的 "page object."