Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/356.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Selenium页面对象。如何从外部源读取@FindBy定位器?_Java_Selenium_Pageobjects - Fatal编程技术网

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

Java Selenium页面对象。如何从外部源读取@FindBy定位器?,java,selenium,pageobjects,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注释中使用硬编码值 但我想动态解析定位器

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() 

相反。

注释是元数据,因此在类加载期间,它们需要在运行时启动时可用。你所寻找的并没有直截了当的答案,但黑客们利用反射,我想我会避免它,如果你需要外部化定位器,我建议你实现对象存储库,在那里你可以在运行时从一些外部源动态读取定位器。

我个人不是
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);
    }
}

我在这里找到了一些线索: 通过实现3个类,最终实现了我想要的: CustomPageFactory、CustomElementLocator、CustomAnnotations

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

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

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"
}
//================自定义页面工厂

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


}
//=======自定义注释

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

}

我想您可能想知道如何按webElements的名称对其执行操作

你需要在构造器中放入一些代码

public AnyConstructorName(AndroidDriver<AndroidElement> driver) {
        this.driver =driver;
        PageFactory.initElements(driver, this);
}
您可以执行支付。单击()

或者任何你想要的手术


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

您的定位器变化很大吗?为什么要从外部文件中读取它们,而不是将它们放在易于找到和组织的页面对象中。我正在尝试为ios和android设备编写平台无关测试,不想复制页面对象2。UI本地化可能是下一个外部化定位器有用的地方3。一些定位器可以在运行时更改(元素按其他顺序排序等),感谢您的回答。我想我明白你的意思了。优点是它通过显式地选择定位器使一切变得清晰。缺点是,一旦你添加了一个新的平台,比如IOS9、IOS10、iPad,你就必须修改所有页面对象的所有开关盒。我更喜欢添加/切换页面配置,并保持代码的完整性。这与您正在做的事情没有太大区别。添加新配置时,必须为配置文件中每个页面上的每个元素添加新的定位器。我在这里也必须这样做。不同的是,我的方法保持页面对象方法的干净性,因为与页面相关的一切都在页面对象中。您的配置文件将很快变得笨拙,因为它包含所有元素的所有定位器乘以您支持的配置数量。我同意-定位器的数量是相同的。但我更喜欢将测试数据与代码分开。谢谢您的帮助,但此解决方案适用于selenium v2.53。我已经用selenium java升级到了3.5.3,再也看不到buildByFromShortFindBy属于Annotations类的方法了,它被移动到AbstractFindBuilder,因此想知道如何连接和重写这个函数来实现我的CustomPageFactory?@BhuvaneshMani你说得对,此代码不再适用于较新版本的selenium
@FindBy(xpath=“//android.widget.TextView[@text='Payments'])
我正在寻找一种方法,不在@FindBy内硬编码定位器,而是从外部源读取定位器
@FindBy(xpath ="//android.widget.TextView[@text='Payments']")

WebElement pay;