获取已找到的 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 创作者可能不提供此功能的任何原因吗?



*尽管这与问题无关,但如果有人想知道您为什么需要此功能,请举两个例子:

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) PageObjectPageFactory 意味着我们在 Page 类 中有 WebElements,但我们没有这些元素的定位器。

2) 如果我使用 webElement.findElement(By) 找到元素作为当前元素的后代,那么即使我将父元素的定位器存储在变量中,我也没有这个后代的定位器。

3) 如果我使用元素的 returns ListfindElements 函数,那么我没有每个特定元素的定位器。

4) 具有元素定位器至少是有用的,因为 ExpectedConditions 以定位器作为参数比 ExpectedConditionsWebElement 作为参数实现得更好。

对我来说,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);
    }
}