Java Selenium页面对象。如何从外部源读取@FindBy定位器?
我只能在页面对象@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
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;