Selenium的页面工厂模式

238 阅读3分钟

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);
    }
}

参考