获取已找到的 WebElement 的 By 定位器
Get the By locator of an already found WebElement
是否有一种优雅的方法来获取 Selenium WebElement 的定位器,我已经 found/identified?
明确问题:我想要 "By locator" 用于查找元素。在这种情况下,我 不 对特定属性或特定定位器(如 css-定位器)感兴趣。
我知道我可以解析 WebElement 的 toString() 方法的结果:
WebElement element = driver.findElement(By.id("myPreciousElement"));
System.out.println(element.toString());
输出将是例如:
[[FirefoxDriver: firefox on WINDOWS (....)] -> id: myPreciousElement]
如果您通过 xpath 找到了您的元素:
WebElement element = driver.findElement(By.xpath("//div[@someId = 'someValue']"));
System.out.println(element.toString());
那么你的输出将是:
[[FirefoxDriver: firefox on WINDOWS (....)] -> xpath: //div[@someId = 'someValue']]
所以我目前编写了自己的方法来解析此输出并为我提供 "recreated" 定位器。
但是是否已经在 Selenium 中实现了一种更优雅的方法来获取用于查找元素的 By 定位器?
到目前为止我找不到一个.
如果您确定 none 开箱即用,您能想出 API 创作者可能不提供此功能的任何原因吗?
*尽管这与问题无关,但如果有人想知道您为什么需要此功能,请举两个例子:
- 如果您使用 PageFactory,您很可能不会将定位器作为页面的成员变量 class,但稍后在处理页面元素时可能需要它们。
- 您正在与 APIs 的人一起工作,他们只使用没有 PageFactory 的页面对象模式,因此希望您交出定位器而不是元素本身。*
tldr;不是默认的,不。您不能从以前找到的 WebElement 中提取 By
。但是,可以通过自定义解决方案。
可以实现自定义解决方案,但 Selenium 不提供这种开箱即用的解决方案。
考虑以下关于“为什么”的问题..
By by = By.id("someId");
WebElement e = driver.findElement(by);
你已经有了 By
对象,所以你不需要调用 e.getBy()
不,没有。我已经实现了一个可能的解决方案作为代理:
public class RefreshableWebElement implements WebElement {
public RefreshableWebElement(Driver driver, By by) {
this.driver = driver;
this.by = by;
}
// ...
public WebElement getElement() {
return driver.findElement(by);
}
public void click() {
getElement().click();
}
// other methods here
}
我使用的是 commons-lang3
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
对于远程网络元素,使用方法如下:
protected String getLocator(WebElement element) {
try {
Object proxyOrigin = FieldUtils.readField(element, "h", true);
Object locator = FieldUtils.readField(proxyOrigin, "locator", true);
Object findBy = FieldUtils.readField(locator, "by", true);
if (findBy != null) {
return findBy.toString();
}
} catch (IllegalAccessException ignored) {
}
return "[unknown]";
}
我写了这个效用函数,它 returns 定位器策略 + 定位器值的字符串组合。
private String getLocatorFromWebElement(WebElement element) {
return element.toString().split("->")[1].replaceFirst("(?s)(.*)\]", "" + "");
}
Selenium 没有提供优雅的方式。这太可怕了
1) PageObject
和 PageFactory
意味着我们在 Page
类 中有 WebElements
,但我们没有这些元素的定位器。
2) 如果我使用 webElement.findElement(By)
找到元素作为当前元素的后代,那么即使我将父元素的定位器存储在变量中,我也没有这个后代的定位器。
3) 如果我使用元素的 returns List
的 findElements
函数,那么我没有每个特定元素的定位器。
4) 具有元素定位器至少是有用的,因为 ExpectedConditions
以定位器作为参数比 ExpectedConditions
以 WebElement
作为参数实现得更好。
对我来说,Selenium 是一个构思不佳且实施不佳的库
目前selenium这边还没有具体的方法可以做到这一点。您可以做的是编写您的自定义方法。只需打印您拥有的 webElement,您就可以了解所使用的选择器类型和路径。
看起来像这样
[[ChromeDriver: chrome on XP (d85e7e220b2ec51b7faf42210816285e)] -> xpath: //input[@title='Search']]
现在,您需要做的是提取定位器及其值。你可以试试这样的
private By getByFromElement(WebElement element) {
By by = null;
//[[ChromeDriver: chrome on XP (d85e7e220b2ec51b7faf42210816285e)] -> xpath: //input[@title='Search']]
String[] pathVariables = (element.toString().split("->")[1].replaceFirst("(?s)(.*)\]", "" + "")).split(":");
String selector = pathVariables[0].trim();
String value = pathVariables[1].trim();
switch (selector) {
case "id":
by = By.id(value);
break;
case "className":
by = By.className(value);
break;
case "tagName":
by = By.tagName(value);
break;
case "xpath":
by = By.xpath(value);
break;
case "cssSelector":
by = By.cssSelector(value);
break;
case "linkText":
by = By.linkText(value);
break;
case "name":
by = By.name(value);
break;
case "partialLinkText":
by = By.partialLinkText(value);
break;
default:
throw new IllegalStateException("locator : " + selector + " not found!!!");
}
return by;
}
当我 运行 需要 By 定位器用于 ExpectedConditions 并且我在页面对象工厂中有我的定位器时,我的解决方案是使用其中包含定位器的字符串,然后构建我的 By对象和元素定位器。
public class PageObject {
private static final String XPATH_NAME = "...";
public @iOSXCUITFindBy(xpath = XPATH_NAME)
List<MobileElement> mobileElementName;
public By getByXPath(){
return new By.ByXPath(XPATH_NAME);
}
public PageObject() {
PageFactory.initElements(driver, this);
}
}
是否有一种优雅的方法来获取 Selenium WebElement 的定位器,我已经 found/identified?
明确问题:我想要 "By locator" 用于查找元素。在这种情况下,我 不 对特定属性或特定定位器(如 css-定位器)感兴趣。
我知道我可以解析 WebElement 的 toString() 方法的结果:
WebElement element = driver.findElement(By.id("myPreciousElement"));
System.out.println(element.toString());
输出将是例如:
[[FirefoxDriver: firefox on WINDOWS (....)] -> id: myPreciousElement]
如果您通过 xpath 找到了您的元素:
WebElement element = driver.findElement(By.xpath("//div[@someId = 'someValue']"));
System.out.println(element.toString());
那么你的输出将是:
[[FirefoxDriver: firefox on WINDOWS (....)] -> xpath: //div[@someId = 'someValue']]
所以我目前编写了自己的方法来解析此输出并为我提供 "recreated" 定位器。
但是是否已经在 Selenium 中实现了一种更优雅的方法来获取用于查找元素的 By 定位器?
到目前为止我找不到一个.如果您确定 none 开箱即用,您能想出 API 创作者可能不提供此功能的任何原因吗?
*尽管这与问题无关,但如果有人想知道您为什么需要此功能,请举两个例子:
- 如果您使用 PageFactory,您很可能不会将定位器作为页面的成员变量 class,但稍后在处理页面元素时可能需要它们。
- 您正在与 APIs 的人一起工作,他们只使用没有 PageFactory 的页面对象模式,因此希望您交出定位器而不是元素本身。*
tldr;不是默认的,不。您不能从以前找到的 WebElement 中提取 By
。但是,可以通过自定义解决方案。
可以实现自定义解决方案,但 Selenium 不提供这种开箱即用的解决方案。
考虑以下关于“为什么”的问题..
By by = By.id("someId");
WebElement e = driver.findElement(by);
你已经有了 By
对象,所以你不需要调用 e.getBy()
不,没有。我已经实现了一个可能的解决方案作为代理:
public class RefreshableWebElement implements WebElement {
public RefreshableWebElement(Driver driver, By by) {
this.driver = driver;
this.by = by;
}
// ...
public WebElement getElement() {
return driver.findElement(by);
}
public void click() {
getElement().click();
}
// other methods here
}
我使用的是 commons-lang3
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
对于远程网络元素,使用方法如下:
protected String getLocator(WebElement element) {
try {
Object proxyOrigin = FieldUtils.readField(element, "h", true);
Object locator = FieldUtils.readField(proxyOrigin, "locator", true);
Object findBy = FieldUtils.readField(locator, "by", true);
if (findBy != null) {
return findBy.toString();
}
} catch (IllegalAccessException ignored) {
}
return "[unknown]";
}
我写了这个效用函数,它 returns 定位器策略 + 定位器值的字符串组合。
private String getLocatorFromWebElement(WebElement element) {
return element.toString().split("->")[1].replaceFirst("(?s)(.*)\]", "" + "");
}
Selenium 没有提供优雅的方式。这太可怕了
1) PageObject
和 PageFactory
意味着我们在 Page
类 中有 WebElements
,但我们没有这些元素的定位器。
2) 如果我使用 webElement.findElement(By)
找到元素作为当前元素的后代,那么即使我将父元素的定位器存储在变量中,我也没有这个后代的定位器。
3) 如果我使用元素的 returns List
的 findElements
函数,那么我没有每个特定元素的定位器。
4) 具有元素定位器至少是有用的,因为 ExpectedConditions
以定位器作为参数比 ExpectedConditions
以 WebElement
作为参数实现得更好。
对我来说,Selenium 是一个构思不佳且实施不佳的库
目前selenium这边还没有具体的方法可以做到这一点。您可以做的是编写您的自定义方法。只需打印您拥有的 webElement,您就可以了解所使用的选择器类型和路径。
看起来像这样
[[ChromeDriver: chrome on XP (d85e7e220b2ec51b7faf42210816285e)] -> xpath: //input[@title='Search']]
现在,您需要做的是提取定位器及其值。你可以试试这样的
private By getByFromElement(WebElement element) {
By by = null;
//[[ChromeDriver: chrome on XP (d85e7e220b2ec51b7faf42210816285e)] -> xpath: //input[@title='Search']]
String[] pathVariables = (element.toString().split("->")[1].replaceFirst("(?s)(.*)\]", "" + "")).split(":");
String selector = pathVariables[0].trim();
String value = pathVariables[1].trim();
switch (selector) {
case "id":
by = By.id(value);
break;
case "className":
by = By.className(value);
break;
case "tagName":
by = By.tagName(value);
break;
case "xpath":
by = By.xpath(value);
break;
case "cssSelector":
by = By.cssSelector(value);
break;
case "linkText":
by = By.linkText(value);
break;
case "name":
by = By.name(value);
break;
case "partialLinkText":
by = By.partialLinkText(value);
break;
default:
throw new IllegalStateException("locator : " + selector + " not found!!!");
}
return by;
}
当我 运行 需要 By 定位器用于 ExpectedConditions 并且我在页面对象工厂中有我的定位器时,我的解决方案是使用其中包含定位器的字符串,然后构建我的 By对象和元素定位器。
public class PageObject {
private static final String XPATH_NAME = "...";
public @iOSXCUITFindBy(xpath = XPATH_NAME)
List<MobileElement> mobileElementName;
public By getByXPath(){
return new By.ByXPath(XPATH_NAME);
}
public PageObject() {
PageFactory.initElements(driver, this);
}
}