我只能在页面对象@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()
代替。
答案 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();
或您想要的任何操作
但只能在您的网页类中执行这些操作