扩展Selenium WebDriver WebElement?

我正在遵循Selenium建议的页面对象模式,但是如何为页面创建更专业的WebElement。 具体来说,我们的页面上有表格,我编写了一些辅助函数来获取表格的特定行,返回表格的内容等。

目前,这是我创建的具有表的页面对象的片段:

public class PermissionsPage { @FindBy(id = "studyPermissionsTable") private WebElement permissionTable; @FindBy(id = "studyPermissionAddPermission") private WebElement addPermissionButton; ... } 

所以,我想要做的是将权限表变成一个更加自定义的WebElement,其中包含我之前提到的一些方法。

例如:

 public class TableWebElement extends WebElement { WebElement table; // a WebDriver needs to come into play here too I think public List<Map> getTableData() { // code to do this } public int getTableSize() { // code to do this } public WebElement getElementFromTable(String id) { // code to do this } } 

我希望这是有道理的,我正在努力解释。 我想我正在寻找的是让这个自定义WebElement做一些特定于表的其他东西的方法。 将此自定义元素添加到页面,并利用Selenium基于注释将webelements连接到页面的方式。

可能吗? 如果是这样,有谁知道如何做到这一点?

我创建了一个结合了所有WebDriver接口的接口:

 public interface Element extends WebElement, WrapsElement, Locatable {} 

它只是包含WebElements在包装元素时可以做的所有事情。

然后是一个实现:

 public class ElementImpl implements Element { private final WebElement element; public ElementImpl(final WebElement element) { this.element = element; } @Override public void click() { element.click(); } @Override public void sendKeys(CharSequence... keysToSend) { element.sendKeys(keysToSend); } // And so on, delegates all the way down... } 

然后,例如一个复选框:

 public class CheckBox extends ElementImpl { public CheckBox(WebElement element) { super(element); } public void toggle() { getWrappedElement().click(); } public void check() { if (!isChecked()) { toggle(); } } public void uncheck() { if (isChecked()) { toggle(); } } public boolean isChecked() { return getWrappedElement().isSelected(); } } 

在我的脚本中使用它时:

 CheckBox cb = new CheckBox(element); cb.uncheck(); 

我还提出了一种包装Element类的方法。 您必须创建一些工厂来替换内置的PageFactory ,但它是可行的,并且它提供了很大的灵活性。

我在我的网站上记录了这个过程:

  • 包装WebElement:第1部分
  • 包装WebElement:第2部分

我还有一个名为selophane的项目受到了这个和其他问题的启发: selophane

您可以使用WebDriver Extensions框架创建自定义WebElements,该框架提供实现WebElement接口的WebComponent类

创建自定义WebElement

 public class Table extends WebComponent { @FindBy(tagName = "tr") List rows; public Row getRow(int row) { return rows.get(row - 1); } public int getTableSize() { return rows.size(); } public static class Row extends WebComponent { @FindBy(tagName = "td") List columns; public WebElement getCell(int column) { return columns.get(column - 1); } } } 

…然后使用@FindBy注释将其添加到PageObject中,并在调用PageFactory.initElements方法时使用WebDriverExtensionFieldDecorator

 public class PermissionPage { public PermissionPage(WebDriver driver) { PageFactory.initElements(new WebDriverExtensionFieldDecorator(driver), this); } @FindBy(id = "studyPermissionsTable") public Table permissionTable; @FindBy(id = "studyPermissionAddPermission") public WebElement addPermissionButton; } 

…然后在测试中使用它

 public class PermissionPageTest { @Test public void exampleTest() { WebDriver driver = new FirefoxDriver(); PermissionPage permissionPage = new PermissionPage(driver); driver.get("http://www.url-to-permission-page.com"); assertEquals(25, permissionPage.permissionTable.getTableSize()); assertEquals("READ", permissionPage.permissionTable.getRow(2).getCell(1).getText()); assertEquals("WRITE", permissionPage.permissionTable.getRow(2).getCell(2).getText()); assertEquals("EXECUTE", permissionPage.permissionTable.getRow(2).getCell(3).getText()); } } 

或者甚至更好地使用WebDriver Extensions PageObject实现

 public class PermissionPage extends WebPage { @FindBy(id = "studyPermissionsTable") public Table permissionTable; @FindBy(id = "studyPermissionAddPermission") public WebElement addPermissionButton; @Override public void open(Object... arguments) { open("http://www.url-to-permission-page.com"); assertIsOpen(); } @Override public void assertIsOpen(Object... arguments) throws AssertionError { assertIsDisabled(permissionTable); assertIsDisabled(addPermissionButton); } } 

和JUnitRunner以及WebElements的静态断言方法

 import static com.github.webdriverextensions.Bot.*; @RunWith(WebDriverRunner.class) public class PermissionPageTest { PermissionPage permissionPage; @Test @Firefox public void exampleTest() { open(permissionPage); assertSizeEquals(25, permissionPage.permissionTable.rows); assertTextEquals("READ", permissionPage.permissionTable.getRow(2).getCell(1)); assertTextEquals("WRITE", permissionPage.permissionTable.getRow(2).getCell(2)); assertTextEquals("EXECUTE", permissionPage.permissionTable.getRow(2).getCell(3)); } } 

作为后续行动,这就是我最终做的事情(如果其他人有同样的问题)。 下面是我作为WebElement的包装器创建的类的片段:

 public class CustomTable { private WebDriver driver; private WebElement tableWebElement; public CustomTable(WebElement table, WebDriver driver) { this.driver = driver; tableWebElement = table; } public WebElement getTableWebElement() { return tableWebElement; } public List getTableRows() { String id = tableWebElement.getAttribute("id"); return driver.findElements(By.xpath("//*[@id='" + id + "']/tbody/tr")); } public List getTableHeader() { String id = tableWebElement.getAttribute("id"); return tableWebElement.findElements(By.xpath("//*[@id='" + id + "']/thead/tr/th")); } .... more utility functions here } 

然后我在任何通过引用它创建的页面中使用它:

 public class TestPage { @FindBy(id = "testTable") private WebElement myTestTable; /** * @return the myTestTable */ public CustomTable getBrowserTable() { return new CustomTable(myTestTable, getDriver()); } 

我唯一不喜欢的是当页面想要获取表格时,它会创建一个“新”表。 我这样做是为了避免更新表中的数据时发生的StaleReferenceExeptions(即单元格更新,添加行,删除行)。 如果有人对我每次请求时如何避免创建新实例有任何建议,而是返回更新的WebElement,这将是很棒的!

旁注: WebElement不是类,它的接口意味着你的类看起来更像是这样:

 public class TableWebElement implements WebElement { 

但在这种情况下,您必须实现WebDriver中的所有方法。 它有点矫枉过正。

这是我如何做到这一点 – 我完全摆脱了selenium提出的建议的PageObjects和通过拥有我自己的类“重新发明轮子”。 我有一个类用于整个应用程序:

 public class WebUI{ private WebDriver driver; private WebElement permissionTable; public WebUI(){ driver = new firefoxDriver(); } public WebDriver getDriver(){ return driver; } public WebElement getPermissionTable(){ return permissionTable; } public TableWebElement getTable(){ permissionTable = driver.findElement(By.id("studyPermissionsTable")); return new TableWebElement(this); } } 

然后我有我的助手课程

 public class TableWebElement{ private WebUI webUI; public TableWebElement(WebUI wUI){ this.webUI = wUI; } public int getTableSize() { // because I dont know exactly what are you trying to achieve just few hints // this is how you get to the WebDriver: WebElement element = webUI.getDriver().findElement(By.id("foo")); //this is how you get to already found table: WebElement myTable = webUI.getPermissionTable(); } } 

测试样本:

  @Test public void testTableSize(){ WebUI web = new WebUI(); TableWebElement myTable = web.getTable(); Assert.assertEquals(myTable.getSize(), 25); } 

为什么要费心扩展WebElement? 您是否正在尝试开发Selenium包? 然后,如果你是扩展该方法是没有意义的,除非你的添加可以适用于你使用的每个web元素,而不仅仅是那些特定于你的表的元素

为什么不做以下事情:

 public class PermissionsPage extends TableWebElement{ ...permissions stuff... } import WebElement public class TableWebElement{ ...custom web elements pertaining to my page... } 

我不知道这是否能回答你的问题,但在我看来,它更像是一个阶级架构问题。

我会创建我称之为“SubPageObject”的东西,它表示PageObject上的PageObject …

优点是您可以为它创建自定义方法,并且您只能在此SubPageObject中初始化您真正需要的WebElements。

为什么不制作一个Web元素包装器? 喜欢这个?

 class WEBElment { public IWebElement Element; public WEBElement(/*send whatever you decide, a webelement, a by element, a locator whatever*/) { Element = /*make the element from what you sent your self*/ } public bool IsDisplayed() { return Element.Displayed; } } // end of class here 

但是你可以让你的课比这更复杂。 只是一个概念

看一下htmlelements框架,看起来就像你需要的那样。 它具有预先实现的常用元素,例如复选框,radiobox,表格,表格等,您可以轻松创建自己的元素并将一个元素插入到其他元素中,从而创建清晰的树形结构。

步骤1)创建一个名为Element的基类,扩展IWrapsElement接口

  public class Element : IWrapsElement { public IWebElement WrappedElement { get; set; } public Element(IWebElement element) { this.WrappedElement = element; } } 

步骤2)为每个自定义元素创建一个类,并从Element类inheritance

  public class Checkbox : Element { public Checkbox(IWebElement element) : base(element) { } public void Check(bool expected) { if (this.IsChecked()!= expected) { this.WrappedElement.Click(); } } public bool IsChecked() { return this.WrappedElement.GetAttribute("checked") == "true"; } } 

用法:

一个)

 Checkbox x = new Checkbox(driver.FindElement(By.Id("xyz"))); x.Check(true) 

b)

  private IWebElement _chkMale{ get; set; } [FindsBy(How = How.Name, Using = "chkMale")] private Checkbox ChkMale { get { return new Checkbox (_chkMale); } } ChkMale.Check(true);