Selenium页面对象重用

时间:2010-10-20 19:20:35

标签: java unit-testing functional-testing selenium-webdriver

我真的很喜欢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<PageElement> widgetElements;
}


public class Screen {
    private List<PageWidget> 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);
    }
}

1 个答案:

答案 0 :(得分:15)

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

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

要解决此限制,我们创建了自己的ElementLocator s。

您可以在测试中使用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<Boolean> e = new ExpectedCondition<Boolean>() {
                public Boolean apply(WebDriver d) {
                    d.findElement(by);
                    return Boolean.TRUE;
                }
            };
            Wait<WebDriver> 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);
        }

    }

}