【问题标题】:How to initialize SelectElements while using PageFactory / FindsBy in Selenium C#?如何在 Selenium C# 中使用 PageFactory / FindsBy 时初始化 SelectElements?
【发布时间】:2015-07-25 00:43:44
【问题描述】:

我正在使用 PageFactory 在 Selenium WebDriver for C# 中构建页面对象模型。

不幸的是,我发现FindsByAttribute 不会初始化SelectElement(HTML <select> 标记/下拉菜单)。到目前为止,我已经碰巧或提出了一些解决它的想法,但没有一个是理想的:

  1. PageFactoryFindsByAttributesealed,所以我不能通过继承它们来强制它。
  2. 在每个方法中手动从IWebElement 实例化SelectElement 是相当混乱和重复的。它还忽略了PageFactory 中明显的内置等待并抛出NoSuchElementExceptions,除非我每次执行此操作时都添加等待 - 这将需要在整个地方重复定位器,从而破坏(部分)目的POM。
  3. SelectElement 属性包装每个IWebElement 属性不会那么混乱,但仍然存在与上述相同的等待问题。

目前最好的选择是#3,并为SelectElement 编写一个包装器,它只是为每个方法添加一个等待。虽然这个解决方案会工作,但它会大量增加每个页面的代码,而不是这个(假设的)漂亮代码:

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

我被一个 wrapper 包装器困住了(我宁愿避免这种情况),并且:

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

SelectorSelectElement 包装器,它还必须接受 IWait<IWebDriver> 以便它可以等待,并在我每次访问它时实例化一个新的 Selector

有更好的方法吗?

编辑: 昏昏欲睡地输入了错误的访问修饰符。固定的。谢谢,@JimEvans。

【问题讨论】:

  • 非常感谢您提出这个问题和回答。 +1

标签: c# selenium drop-down-menu selenium-webdriver pageobjects


【解决方案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,如果您需要访问IWebElement接口提供的元素的方法和属性,您只需调用WrappedElement属性即可。

.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 对象。否则,您所实现的只是一个“页面包装器”,这是一种完全有效的方法,而不是人们所说的“页面对象”。

【讨论】:

  • 总是喜欢你的回答。有新东西要学。但是,RetryingElementLocator 背后的逻辑是什么?就大量 Ajax 页面或 Angular 而言,定位元素会有所帮助吗?
  • 奇怪的是,虽然我没有检查 PageFactory 代码来寻找等待,但我没想到会有一个,然而,它仍然等待元素加载。我知道严格的页面对象模式不会暴露元素。它们现在才暴露出来,因为我在构建 POM 的过程中还很早,发现它暂时很方便。
  • 谢谢! RetryingElementLocator 确实让我不必包装 SelectElement。我希望有一种方法可以得到更清洁的东西(比如直接在SelectElement 上的FindsBy),但没有让我感到惊讶。并不是说我现在一定需要它,但是您是否有更多关于IPageObjectMemberDecorator 用法的信息?甚至 Google 也只有 10 个结果。
猜你喜欢
  • 1970-01-01
  • 2012-01-14
  • 2022-06-10
  • 2011-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多