Selenium页面对象。如何从外部源读取@FindBy定位器?

时间:2016-10-07 10:21:50

标签: java selenium pageobjects

我只能在页面对象@FindBy注释中使用硬编码值 但我想动态地解决定位器。

public class LoginPage extends BasePage {

    // hardocded value works ok
    @FindBy(name = "login field")
    WebElement usernameFld;

    // does not compile
    // this is a kind of what I would like to have  
    @FindBy( getLocatorFromExternalSource() ) 
    WebElement passwordFld;

}

我看过一些帖子提到这些事情可以通过实现自定义注释/装饰/工厂来解决,但还没有找到示例。

问题: 有人可以提供一个示例如何实现自定义ElementLocatorFactory,以便动态解析定位器吗?

我知道我可以使用简单的旧式调用:

driver.findElement( getLocatorFromExternalSource("passwordFld") ).click()

但我想用

passwordFld.click() 

代替。

4 个答案:

答案 0 :(得分:0)

注释是MetaData,因此它们需要在类加载期间在运行时启动时可用。你寻求的东西没有直接的答案,但黑客在那里使用思考,恕我直言,我会避免它,如果你需要外化定位器,那么我建议你实现对象存储库,你可以在运行时从运行时读取你的定位器一些外部来源。

答案 1 :(得分:0)

我个人不是PageFactory的忠实粉丝,但如果你已经在使用它,我会做类似下面的事情。

public class LoginPage extends BasePage
{
    WebDriver driver;
    WebElement usernameFld;
    @FindBy(name = "login field")
    WebElement usernameIOs;

    @FindBy(name = "something else")
    WebElement usernameAndroid;

    public LoginPage(WebDriver webDriver, Sites site)
    {
        this.driver = webDriver;
        switch (site)
        {
            case IOS:
                usernameFld = usernameIOs;
                break;
            case ANDROID:
                usernameFld = usernameAndroid;
                break;
        }
    }

    public void setUsername(String username)
    {
        usernameFld.sendKeys(username);
    }
}

其他地方你要定义

public enum Sites
{
    IOS, ANDROID
}

我更喜欢声明定位器,然后根据需要刮掉元素。我发现这样性能更高,并且您可以减少过时的元素问题等等。

public class LoginPage extends BasePage
{
    WebDriver driver;
    By usernameLocator;
    By usernameIOsLocator = By.name("login field");
    By usernameAndroidLocator = By.name("something else");

    public LoginPage(WebDriver webDriver, Sites site)
    {
        this.driver = webDriver;
        switch (site)
        {
            case IOS:
                usernameLocator = usernameIOsLocator;
                break;
            case ANDROID:
                usernameLocator = usernameAndroidLocator;
                break;
        }
    }

    public void setUsername(String username)
    {
        driver.findElement(usernameLocator).sendKeys(username);
    }
}

答案 2 :(得分:0)

我在这里找到了一些线索:https://stackoverflow.com/a/3987430/1073584  最终设法通过实现3个类来获得我想要的东西: CustomPageFactory,CustomElementLocator,CustomAnnotations。

现在我可以将我的定位器外部化为typesafe配置 并使用以下代码初始化页面对象

// =======登录页面

public class LoginPage extends AbstractPage {
    Config config;

    @FindBy(using = "CONFIG") // take locator from config
    WebElement passwordFld;

    // .. other fields skipped


    public LoginPage(WebDriver webDriver, Config config) throws IOException {
        super(webDriver);
        this.config = config;
        PageFactory.initElements(new CustomPageFactory(webDriver, config), this);
    }

}

// ===== login.page.typesafe.config:

passwordFld = {
   name = "password field"
}

// ============= CustomPageFactory

package com.company.pages.support;

import com.typesafe.config.Config;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.pagefactory.ElementLocator;
import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;

import java.lang.reflect.Field;

public class CustomPageFactory implements ElementLocatorFactory {
    private Config config;
    private WebDriver driver;

    public CustomPageFactory(WebDriver driver, Config config) {
        this.driver = driver;
        this.config = config;
    }

    public ElementLocator createLocator(Field field) {
        return new CustomElementLocator(driver, field, config);
    }
}

// ================= CustomElementLocator

package com.company.pages.support;

import com.typesafe.config.Config;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.support.pagefactory.DefaultElementLocator;

import java.lang.reflect.Field;

public class CustomElementLocator extends DefaultElementLocator {

    private Config config;

    public CustomElementLocator(SearchContext searchContext, Field field, Config config) {
        super(searchContext, new CustomAnnotations(field, config));
        this.config = config;
    }


}

// ====== CustomAnnotations

package com.company.pages.support;

import com.typesafe.config.Config;
import com.typesafe.config.ConfigObject;
import org.openqa.selenium.By;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.pagefactory.Annotations;

import java.lang.reflect.Field;

public class CustomAnnotations extends Annotations {

    Config config;

    public CustomAnnotations(Field field, Config config) {
        super(field);
        this.config = config;
    }

    @Override
    protected By buildByFromShortFindBy(FindBy findBy) {

        if (findBy.using().equals("CONFIG")) {

            if (null != config) {

                ConfigObject fieldLocators = config.getObject(getField().getName());

                if (fieldLocators.keySet().contains("className"))
                    return By.className(fieldLocators.get("className").unwrapped().toString());

                if (fieldLocators.keySet().contains("css"))
                    return By.cssSelector(fieldLocators.get("css").unwrapped().toString());

                if (fieldLocators.keySet().contains("id"))
                    return By.id(fieldLocators.get("id").unwrapped().toString());

                if (fieldLocators.keySet().contains("linkText"))
                    return By.linkText(fieldLocators.get("linkText").unwrapped().toString());

                if (fieldLocators.keySet().contains("name"))
                    return By.name(fieldLocators.get("name").unwrapped().toString());

                if (fieldLocators.keySet().contains("partialLinkText"))
                    return By.partialLinkText(fieldLocators.get("partialLinkText").unwrapped().toString());

                if (fieldLocators.keySet().contains("tagName"))
                    return By.tagName(fieldLocators.get("tagName").unwrapped().toString());

                if (fieldLocators.keySet().contains("xpath"))
                    return By.xpath(fieldLocators.get("xpath").unwrapped().toString());
            }

        }

        return super.buildByFromShortFindBy(findBy);
    }

}

答案 3 :(得分:0)

我认为您想知道通过名称

对webElements执行操作

你需要在构造函数中加入一些代码

public AnyConstructorName(AndroidDriver<AndroidElement> driver) {
        this.driver =driver;
        PageFactory.initElements(driver, this);
}
如果您接受来自其他类的驱动程序,则上面的代码将起作用 如果不是这种情况,请从构造函数中删除this.driver = driver

初始化像

这样的网络元素
@FindBy(xpath ="//android.widget.TextView[@text='Payments']")

WebElement pay;
你可以执行pay.click();

或您想要的任何操作

但只能在您的网页类中执行这些操作