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

问题描述 投票:0回答:5

我只能在页面对象@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() 

相反。

java selenium pageobjects
5个回答
0
投票

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


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

0
投票

我在这里找到了一些线索:https://stackoverflow.com/a/3987430/1073584 最后通过实现 3 个类得到了我想要的东西: 自定义页面工厂、自定义元素定位器、自定义注释。

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

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

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

}

//======登录.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);
    }
}

//================== 自定义元素定位器

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

}

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

或任何你想要的操作

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


0
投票

要读取 @findby 注释的属性值,即 (xpath, id),您可以使用外部类,您可以将 (xpath, id) 值声明为 public static Final 并使用它们的类名将它们访问到 @findby 注释中(.) 变量名。

示例:

Xpath.java

public class Xpath {
     public static final String email = "//input[starts-with(@id,'email_address')]";
     public static final String password = "//input[starts-with(@id,'password')]";      
}

登录.Java

public class LoginPage extends BasePage {
    // Uses of xpath, id is depending upon your own requirement
    @FindBy(xpath=Xpath.email)
    WebElement usernameFld;

    @FindBy(xpath=Xpath.password) 
    WebElement passwordFld;   
}

注:

我这里以X路径为例,您可以根据您的要求使用(xpath, id)。

© www.soinside.com 2019 - 2024. All rights reserved.