Web 应用程序 UI 中的最小更改都会影响Selenium 测试脚本,为了优化测试脚本维护效率,测试人员应该尽可能减少产品变更对测试脚本的影响,其中最大的挑战是让测试用例与脚本中使用的 Web 元素“更加分离”。 而Selenium 中的页面对象模型,也称为 POM,是 Selenium 中流行的设计模式,用于创建用于存储 WebElement 的对象存储库。就是解决这个问题的一种方法。
Selenium 中的页面工厂是什么
Selenium 中的页面工厂是 WebDriver 页面对象模型 (POM) 的高效内置实现,作为页面对象的扩展,页面工厂通过利用 Selenium 中的 PageFactory 类,简化测试人员与 Web 元素的交互。
Selenium中页面工厂的优势
在 Selenium 中使用 PageFactory 进行 Web 自动化测试的优点是:
- 减少代码重复:页面工厂允许更简洁和可重用的页面对象。这减少了创建和维护页面对象所需的样板代码量,从而减少了代码重复并提高了代码可维护性。
- 提高代码可读性:PageFactory 通过将 Web 元素的初始化与测试代码分开,使自动化代码更具可读性。这使得更容易理解代码的意图以及与网页的交互。
- 改进的测试维护:PageFactory 通过将页面对象初始化代码与测试代码分离,使得在网页发生更改时更新代码变得更加容易,从而降低了错误风险并使测试维护更加高效。
- 更好的测试性能:PageFactory 可以通过减少在网页上定位 Web 元素的开销来提高测试性能。这是通过每次测试仅初始化一次页面对象来实现的,而不是为每个测试方法初始化一次。
PageFactory定位元素
页面工厂模式通过注解定位元素,通过这些注解注释Page类的成员变量,而PageFactory.InitElements(driver, classname.class) 只是设置这些代理,它实际上并没有找到元素。
- @FindBy: 使用 @FindBy 注释定义的 WebElement 是有效的代理。每次使用 WebElement 执行操作时,它都会使用 @FindBy 注释中的定位器再次在页面上查找该元素。这用于仅通过一个定义的标准来识别元素。
- @FindBys: 这用于指向具有多个条件的网页元素,并且必须匹配所有条件。
- @FindAll:这与@FindBys类似,但它需要至少匹配一个条件。条件之间是 or 的关系。
- @CacheLookUp:缓存单个 WebElement,它在第一次运行后将 WebElement 保留在缓存中。
@FindBys( {
@FindBy(className = "class1")
@FindBy(className = "class2")
} )
private List<WebElement> elementsWithBoth_class1ANDclass2;
@FindAll({
@FindBy(className = "class1")
@FindBy(className = "class2")
})
private List<WebElement> elementsWithEither_class1ORclass2
PageFactory模式 example
import org.openqa.selenium.*;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;
/**
* description:
* page基类,用于封装一些公共方法,操作元素,获取元素等,所有page都继承该类。
* 方便后续维护,如添加新的公共方法,只需在该类中添加即可,不需要每个page都添加,减少代码冗余
*/
public class BasePage {
public WebDriver driver;
public WebDriverWait wait;
public BasePage(WebDriver driver) {
this.driver = driver;
wait = new WebDriverWait(driver, Duration.ofSeconds(15));
PageFactory.initElements(driver, this);
}
public void clickByWebElement(WebElement webElement) {
wait.until(ExpectedConditions.elementToBeClickable(webElement)).click();
}
public void writeText(WebElement element, String text) {
waitVisibility(element);
element.clear();
element.sendKeys(text);
}
public String readText(WebElement element) {
return waitVisibility(element).getText();
}
public WebElement waitVisibility(WebElement webElement) {
return wait.until(ExpectedConditions.visibilityOf(webElement));
}
public WebElement waitClickable(WebElement webElement) {
return wait.until(ExpectedConditions.elementToBeClickable(webElement));
}
}
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
/**
* description:
* PageObject 对外提供的方法:
* 1. 获取数据
* 2. 操作元素
* 3. 返回另外一个PageObject
*/
public class LoginPO extends BasePage {
@FindBy(css = "input[name='username']")
private WebElement inputUsername;
@FindBy(css = "input[name='password']")
private WebElement inputPassword;
@FindBy(css = "button[class^='el-button']")
private WebElement buttonSign;
public LoginPO(WebDriver driver) {
super(driver);
}
public LoginPO open(String loginUrl) {
driver.get(loginUrl);
return this;
}
public HomePage login(String username, String password) {
writeText(inputUsername, username);
writeText(inputPassword, password);
click(buttonSign);
return new HomePage(driver);
}
}