
本文旨在解决selenium自动化测试中常见的`by`定位器类型不匹配问题,特别是当自定义方法返回`by`对象时,如何正确地将其应用于`expectedconditions`或`findelement`方法。核心在于理解`by`类方法的参数类型,避免将已封装的`by`对象再次传递给期望`string`参数的`by`工厂方法,确保代码的类型一致性和功能正确性。
在Selenium自动化测试中,org.openqa.selenium.By类是用于定位页面元素的核心工具。它提供了一系列静态工厂方法,如By.id()、By.name()、By.xpath()、By.cssSelector()等,用于根据不同的策略创建定位器对象。然而,初学者在使用这些定位器时,尤其是在将定位器封装到单独的方法中时,常常会遇到类型不匹配的错误,例如'id(java.lang.String)' in 'org.openqa.selenium.By' cannot be applied to '(org.openqa.selenium.By)'。
理解问题根源
这个错误信息清晰地指出了问题所在:By.id()方法期望接收一个java.lang.String类型的参数(即元素的ID字符串),但实际传递给它的却是一个org.openqa.selenium.By类型的对象。这通常发生在尝试对一个已经封装好的By对象再次调用By.id()等工厂方法时。
考虑以下Java Selenium代码示例,它展示了导致此错误的典型场景:
package adta;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait; // 假设BasePage中定义了wait
public class LoginPage extends BasePage { // 假设BasePage提供了WebDriver driver和WebDriverWait wait
public LoginPage(WebDriver driver) {
super(driver);
}
// 封装了一个返回By对象的定位器方法
public By getUserNameLocator(){
return By.id("user-name"); // 返回一个By类型的对象
}
public void open(){
driver.get("https://www.saucedemo.com/");
}
// 存在问题的isLoaded方法
public boolean isLoaded(){
// 错误发生在这里:By.id()期望String,但getUserNameLocator()返回By对象
return wait.until(ExpectedConditions.visibilityOfElementLocated(By.id(getUserNameLocator()))).isDisplayed();
}
public void login(String username, String password) {
// 同样存在问题:driver.findElement()期望By对象,但By.id()再次包装
driver.findElement(By.id(getUserNameLocator())).sendKeys(username);
driver.findElement(By.id("password")).sendKeys(password);
driver.findElement(By.id("login-button")).click();
}
}在上述代码的isLoaded()方法中,getUserNameLocator()方法已经返回了一个完整的By对象(By.id("user-name"))。当我们将这个By对象作为参数传递给By.id()时,编译器会报错,因为它发现By.id()接收的是一个By对象,而不是它所期望的String。
正确使用By定位器
解决这个问题的关键在于理解ExpectedConditions.visibilityOfElementLocated()和driver.findElement()等方法都直接接受一个By类型的对象作为参数。因此,如果你的自定义方法已经返回了一个By对象,你应当直接使用它,而无需再次通过By.id()或其他By工厂方法进行“二次包装”。
以下是修正后的LoginPage类代码:
package adta;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait; // 假设BasePage中定义了wait
public class LoginPage extends BasePage {
public LoginPage(WebDriver driver) {
super(driver);
}
public By getUserNameLocator(){
return By.id("user-name"); // 返回一个By类型的对象
}
public void open(){
driver.get("https://www.saucedemo.com/");
}
// 修正后的isLoaded方法
public boolean isLoaded(){
// 直接使用getUserNameLocator()返回的By对象
return wait.until(ExpectedConditions.visibilityOfElementLocated(getUserNameLocator())).isDisplayed();
}
public void login(String username, String password) {
// 直接使用getUserNameLocator()返回的By对象
driver.findElement(getUserNameLocator()).sendKeys(username);
driver.findElement(By.id("password")).sendKeys(password); // 这个是正确的,因为By.id("password")直接创建了一个By对象
driver.findElement(By.id("login-button")).click();
}
}通过上述修改,isLoaded()和login()方法中的getUserNameLocator()直接返回一个By类型的对象,这个对象可以直接传递给ExpectedConditions.visibilityOfElementLocated()和driver.findElement()方法,从而避免了类型不匹配的错误。
最佳实践与总结
- 封装定位器:将定位器封装在方法中(如Page Object Model设计模式)是一个良好的实践,它提高了代码的可维护性和可读性。确保这些方法返回By类型对象。
- 理解By工厂方法:By.id(String id)、By.name(String name)等方法是用于根据字符串参数创建By对象的工厂方法。它们不接受另一个By对象作为参数。
-
直接使用By对象:当一个方法已经返回了By对象时,可以直接将其传递给期望By类型参数的Selenium API方法,例如WebDriver.findElement(By by)、WebDriverWait.until(ExpectedCondition
condition)中的ExpectedConditions.visibilityOfElementLocated(By by)等。 - 避免冗余包装:不要将一个已经封装好的By对象再次传递给By类的工厂方法,这会导致类型错误。
通过遵循这些原则,您可以更有效地在Selenium自动化测试中管理和使用定位器,编写出更健壮、更易于维护的代码。










