【问题标题】:Selenium Page Object ReuseSelenium 页面对象重用
【发布时间】:2010-10-20 19:20:35
【问题描述】:

我真的很喜欢 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 的能力?

Edit 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);
        }
}

Edit 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);
    }
}

【问题讨论】:

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


    【解决方案1】:

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

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

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

    您可以在测试中使用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);
            }
    
        }
    
    }
    

    【讨论】:

    • 这绝对让我走上了正轨。我希望将其扩展得更远,而不仅仅是 ID(您可以注入任何需要的方法,然后定位)。我的问题是我没有看到这些字段是如何创建的——这导致我无法正确掌握 ElementLocator。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-20
    • 1970-01-01
    相关资源
    最近更新 更多