Selenium页面对象重用

我真的很喜欢selenium 2按照惯例推动你使用PageObjects作为POJO,然后简单地使用PageFactory来实例化这个类中的字段。

我发现限制的是我们在许多不同的页面上重用了很多元素。 最大的问题是这些重用的组件在出现在不同页面时没有相同的id / name; 但是我们为每个测试运行的测试是相同的。

例如,我们在很多地方收集日期。 因此,此示例页面对象可能是(月,日字段已删除):

public class DatePageObject { private WebDriver driver; DatePageObject(WebDriver driver) { this.driver = driver; } @FindBy( id = "someIdForThisInstance") private WebElement year; public void testYearNumeric() { this.year.sendKeys('aa'); this.year.submit(); //Logic to determine Error message shows up } } 

然后我可以使用下面的代码简单地测试一下:

 public class Test { public static void main(String[] args) { WebDriver driver = new FirefoxDriver(); DatePageObject dpo = PageFactory.initElements(driver, DriverPageObject.class); driver.get("Some URL"); dpo.testYearNumeric(); } } 

我真正想做的是有一个设置,通过Spring我可以将id / name / xpath等注入到应用程序中。

有没有办法可以做到这一点,而不会失去使用PageFactory的能力?

编辑1 – 添加理想的基础级别类,处理自定义定位器和工厂。

 public class PageElement { private WebElement element; private How how; private String using; PageElement(How how, String using) { this.how = how; this.using = using; } //Getters and Setters } public class PageWidget { private List widgetElements; } public class Screen { private List fullPage; private WebDriver driver; public Screen(WebDriver driver) { this.driver = driver; for (PageWidget pw : fullPage) { CustomPageFactory.initElements(driver, pw.class); } } 

编辑2 – 就像注释一样,只要您运行Selenium 2.0.a5或更高版本,您现在可以为驱动程序提供隐式超时值。

所以你可以用以下代码替换代码:

 private class CustomElementLocator implements ElementLocator { private WebDriver driver; private int timeOutInSeconds; private final By by; public CustomElementLocator(WebDriver driver, Field field, int timeOutInSeconds) { this.driver = driver; this.timeOutInSeconds = timeOutInSeconds; CustomAnnotations annotations = new CustomAnnotations(field); this.by = annotations.buildBy(); driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS); //Set this value in a more realistic place } public WebElement findElement() { return driver.findElement(by); } } 

您可以构建公共Web元素的页面对象(刚刚发明了这个名称:)) – 每个CWE将代表在不同页面上使用的“小部件”。 在您的示例中,这将是某种日期窗口小部件 – 它包含年,月和日。 基本上它将是一个Page Object。

PageFactory要求在@FindBy注释中使用字符串常量。

为了解决这个限制,我们创建了自己的ElementLocator

您可以在测试中使用DateWidget

 .... DateWidget widget = new DateWidget(driver, "yearId", "monthId", "dayId"); .... public void testYearNumeric() { widget.setYear("aa"); widget.submit(); //Logic to determine Error message shows up // ... and day widget.setDay("bb"); widget.submit(); //Logic to determine Error message shows up } 

DateWidget类包含自定义定位器和注释解析器:

 package pagefactory.test; import java.lang.reflect.Field; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; import org.openqa.selenium.support.pagefactory.Annotations; import org.openqa.selenium.support.pagefactory.ElementLocator; import org.openqa.selenium.support.pagefactory.ElementLocatorFactory; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.Wait; import org.openqa.selenium.support.ui.WebDriverWait; public class DateWidget { // These constants are used to identify that they should be changed to the actual IDs private static final String YEAR_ID = "$YEAR_ID$"; private static final String MONTH_ID = "$MONTH_ID$"; private static final String DAY_ID = "$DAY_ID$"; // Elements whose ids will be replaced during run-time /** Year element */ @FindBy(id = YEAR_ID) private WebElement year; /** Month element */ @FindBy(id = MONTH_ID) private WebElement month; /** day element */ @FindBy(id = DAY_ID) private WebElement day; // The ids of the elements /** ID of the year element */ private String yearId; /** ID of the month element */ private String monthId; /** ID of the day element */ private String dayId; public DateWidget(WebDriver driver, String yearId, String monthId, String dayId) { this.yearId = yearId; this.monthId = monthId; this.dayId = dayId; PageFactory.initElements(new CustomLocatorFactory(driver, 15), this); } public String getYear() { return year.getValue(); } public void setYear(String year) { setValue(this.year, year); } public String getMonth() { return month.getValue(); } public void setMonth(String month) { setValue(this.month, month); } public String getDay() { return day.getValue(); } public void setDay(String day) { setValue(this.day, day); } public void submit() { year.submit(); } private void setValue(WebElement field, String value) { field.clear(); field.sendKeys(value); } private class CustomLocatorFactory implements ElementLocatorFactory { private final int timeOutInSeconds; private WebDriver driver; public CustomLocatorFactory(WebDriver driver, int timeOutInSeconds) { this.driver = driver; this.timeOutInSeconds = timeOutInSeconds; } public ElementLocator createLocator(Field field) { return new CustomElementLocator(driver, field, timeOutInSeconds); } } private class CustomElementLocator implements ElementLocator { private WebDriver driver; private int timeOutInSeconds; private final By by; public CustomElementLocator(WebDriver driver, Field field, int timeOutInSeconds) { this.driver = driver; this.timeOutInSeconds = timeOutInSeconds; CustomAnnotations annotations = new CustomAnnotations(field); this.by = annotations.buildBy(); } @Override public WebElement findElement() { ExpectedCondition e = new ExpectedCondition() { public Boolean apply(WebDriver d) { d.findElement(by); return Boolean.TRUE; } }; Wait w = new WebDriverWait(driver, timeOutInSeconds); w.until(e); return driver.findElement(by); } } private class CustomAnnotations extends Annotations { public CustomAnnotations(Field field) { super(field); } @Override protected By buildByFromShortFindBy(FindBy findBy) { if (!"".equals(findBy.id())) { String id = findBy.id(); if (id.contains(YEAR_ID)) { id = id.replace(YEAR_ID, yearId); return By.id(id); } else if (id.contains(MONTH_ID)) { id = id.replace(MONTH_ID, monthId); return By.id(id); } else if (id.contains(DAY_ID)) { id = id.replace(DAY_ID, dayId); return By.id(id); } } return super.buildByFromShortFindBy(findBy); } } }