自动化测试框架之所以形成,是因为:
1)业务复杂度提升 → 手工测试扛不住,需要自动化。
2)前端 UI 不稳定 → 需要 Page 模型、元素自动初始化。
3)多环境、多平台 → 需要统一框架体系。
4)敏捷与 CI/CD → 需要自动执行流程。
5)团队规模扩大 → 需要规范化、可维护、可复用的架构。
形成 Selenium + Page Object + Page Factory + 数据驱动 + 报告系统 的主流自动化测试框架体系。
一、WebDriver与HTML 的关系及元素获取
前言
1.1 核心关系

HTML是网页的骨架,WebDriver无法直接识别网页视觉效果,只能通过解析HTML元素的属性,定位元素并执行操作,本质是“通过代码操作HTML元素,模拟用户行为”。
1.2 元素获取方法(核心:选择器 + XPath)
获取元素是自动化测试的基础,常用两种方式,结合实际场景说明,直接用于代码开发:
1.2.1 常用选择器定位(简洁高效)
通过HTML元素的核心属性定位,优先级:id(唯一)> class > name > tagName,代码示例如下:
id:就是元素的id属性值,class:就是元素的class属性值(有多个),name:就是元素的name属性值,tagName:就是元素的标签名(如a标签,p标签)

// 1. id定位(最常用,元素id唯一,精准无冲突)
driver.findElement(By.id("username")); // 定位id为username的元素
// 2. class定位(元素class属性,可能多个元素共用)
driver.findElement(By.className("login-btn")); // 定位class为login-btn的元素
// 3. name定位(元素name属性,常用于表单元素)
driver.findElement(By.name("password")); // 定位name为password的元素
// 4. tagName定位(元素标签名,适合同类标签批量定位)
driver.findElements(By.tagName("input")); // 定位所有input标签元素(如输入框)1.2.2 XPath定位(万能定位,适配所有场景)
当元素无id、class等清晰属性时,使用XPath(通过元素路径/属性匹配定位),核心语法+示例:


// 1. 绝对路径(不推荐,页面结构变化易失效)
driver.findElement(By.xpath("/html/body/div[1]/div[2]/input"));
// 2. 相对路径(推荐,灵活稳定)
// 语法://标签名[@属性名="属性值"]
driver.findElement(By.xpath("//input[@id='username']")); // 等价于id定位
driver.findElement(By.xpath("//button[@class='login-btn']")); // 等价于class定位
// 3. 模糊匹配(元素属性值不完整时使用)
driver.findElement(By.xpath("//input[contains(@placeholder,'请输入')]")); // 匹配placeholder包含“请输入”的input元素
driver.findElement(By.xpath("//a[text()='登录']")); // 匹配文本为“登录”的a标签元素二、WebDriver的事件处理
前言
2.1 常见事件处理(核心必记)
事件即用户对网页的操作,WebDriver模拟所有常用用户行为,核心5种事件,结合bilibili网站(https://www.bilibili.com/)案例演示,代码注释详细可直接运行。

- 点击事件:click() → 模拟用户点击(按钮、链接等);
- 输入事件:sendKeys("内容") → 向输入框输入文本;
- 清空事件:clear() → 清空输入框残留内容;
- 提交事件:submit() → 提交表单(适用于登录、注册页面);
- 鼠标悬停:Actions类 → 模拟鼠标悬停在元素上(如导航栏)。
2.2 实战案例:bilibili网站事件处理
package cn.yangeit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
import java.time.Duration;
public class BilibiliEventTest {
public static void main(String[] args) {
//手动指定驱动路径(Windows示例)
System.setProperty("webdriver.chrome.driver", "D:\\javasoftware\\ChromeDriver\\chromedriver.exe");
//指定Chrome安装位置,配置OPtion
// ChromeOptions options = new ChromeOptions();
// options.setBinary("D:\\javasoftware\\Chrome\\Application\\chrome.exe");
WebDriver driver = new ChromeDriver();
driver.manage().window().maximize();
// 智能等待,避免元素找不到
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
try {
// 1. 打开mianfeiziti网站(目标案例网站)
driver.get("https://movie.douban.com/");
// 2. 点击事件:点击首页“登录”按钮(XPath定位)
wait.until(ExpectedConditions.visibilityOfElementLocated(
By.xpath("//*[@id=\"db-global-nav\"]/div/div[1]/a"))).click();
// 注释:等待登录按钮显示后点击,避免页面加载延迟导致报错
// 3. 输入事件:输入账号密码(定位登录弹窗中的输入框)
wait.until(ExpectedConditions.visibilityOfElementLocated(By.className("global-phone-input-phone"))).sendKeys("18684811161"); // 输入账号
driver.findElement(By.className("account-form-input")).sendKeys("1234"); // 输入验证码
Thread.sleep(5000);
// 4. 清空事件:清空账号输入框(模拟重新输入场景)
driver.findElement(By.className("global-phone-input-phone")).clear();
driver.findElement(By.className("global-phone-input-phone")).sendKeys("123456"); // 重新输入账号
// 5. 表单提交事件:提交登录表单(等价于点击登录按钮)
// driver.findElement(By.className("btn btn-phone btn-active")).submit();//这这样写会报错的,className只能写一个,如果要多个,使用下面方案
//方法 1:CSS 选择器 + click ()(最标准、最稳定)
driver.findElement(By.cssSelector(".btn.btn-phone.btn-active")).click();
//方法 2:XPath + click()
// driver.findElement(By.xpath("//*[@class='btn btn-phone btn-active']")).click();
// 6. 鼠标悬停事件:悬停在二维码上(Actions类)
// 注释:需导入org.openqa.selenium.interactions.Actions包
Actions actions = new Actions(driver);
// 获取二维码元素
WebElement element = driver.findElement(By.id("expand-qr"));
// 执行鼠标悬停操作
actions.moveToElement(element).perform();
System.out.println("事件处理演示完成");
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
} finally {
driver.quit(); // 关闭浏览器,释放资源
}
}
}
三、Page Object与Page Factory
3.1 Page Object
前言
3.1.1 为什么要学这两个技术?(核心原因)
初学阶段,所有元素定位、操作都写在一个类里,存在3个核心问题:
- 代码杂乱:元素定位与测试逻辑混在一起,可读性差、难以维护;
- 修改繁琐:页面元素(如id、class)一旦修改,所有用到该元素的代码都要逐一修改;
- 无法复用:同一个页面的操作(如登录),不能在多个测试用例中重复调用。
Page Object(PO)与Page Factory 就是为解决以上问题而生,是企业级自动化测试的主流规范:👈 👈
- Page Object:将页面元素和操作封装成类,实现“页面与测试用例分离”;
- Page Factory:是PO模式的升级版,用注解简化元素定位,提升代码简洁度和开发效率。
3.1.2 实战案例:https://www.mianfeiziti.com/ 登录测试
以免费字体网登录功能为案例,分别演示两种模式的用法,代码简洁、注释清晰,可直接运行。
核心:一个页面对应一个类,封装元素和操作,测试用例只调用方法。
- 页面类:DouBanLoginPage.java(封装豆瓣登录页面元素和操作)
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
import java.time.Duration;
public class DouBanLoginPage {
private WebDriver driver;
private WebDriverWait wait;
// 元素定位(复用提供的豆瓣登录元素,只定义一次,修改时只需改这里)
private By loginBtn = By.xpath("//*[@id=\"db-global-nav\"]/div/div[1]/a"); // 首页登录按钮
private By phoneInput = By.className("global-phone-input-phone"); // 手机号输入框
private By codeInput = By.className("account-form-input"); // 验证码输入框
private By submitBtn = By.cssSelector(".btn.btn-phone.btn-active"); // 登录提交按钮(CSS定位,解决多class问题)
// 构造方法:初始化驱动和等待对象
public DouBanLoginPage(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
}
// 封装操作方法(供测试用例调用,对应提供的代码逻辑)
// 1. 打开豆瓣电影网
public void openDouBan() {
driver.get("https://movie.douban.com/");
driver.manage().window().maximize();
}
// 2. 点击首页登录按钮
public void clickLoginBtn() {
wait.until(ExpectedConditions.visibilityOfElementLocated(loginBtn)).click();
}
// 3. 输入手机号
public void inputPhone(String phone) {
wait.until(ExpectedConditions.visibilityOfElementLocated(phoneInput)).sendKeys(phone);
}
// 4. 输入验证码
public void inputCode(String code) {
driver.findElement(codeInput).sendKeys(code);
}
// 6. 点击提交登录(解决多class报错问题,用CSS定位+click)
public void clickSubmitBtn() {
driver.findElement(submitBtn).click();
}
// 8. 完整登录流程(组合操作,简化测试用例调用)
public void login(String phone, String code) throws InterruptedException {
openDouBan();
clickLoginBtn();
inputPhone(phone);
inputCode(code);
clickSubmitBtn();
Thread.sleep(5000);
}
}- 测试用例类:DouBanLoginTest.java(调用页面类,实现登录功能)
public class DouBanLoginTest {
public static void main(String[] args) throws InterruptedException {
// 配置驱动(替换为自己的chromedriver路径)
System.setProperty("webdriver.chrome.driver", "D:\\javasoftware\\ChromeDriver\\chromedriver.exe");
WebDriver driver = new ChromeDriver();
// 创建页面类对象,调用登录方法
DouBanLoginPage douBanLoginPage = new DouBanLoginPage(driver);
douBanLoginPage.login("18684811161", "1234"); // 替换为实际手机号和验证码
System.out.println("豆瓣登录事件处理演示完成");
driver.quit(); // 关闭浏览器,释放资源
}
}Page Object:解决代码杂乱、难以维护的问题,核心“一页一类”;
Page Factory:简化PO模式的元素定位,用注解替代手动By语句,企业主流;
3.2 Page Factory
前言
3.2.1 Page Factory是什么?
Page Factory是Page Object的升级版,用注解简化元素定位,提升代码简洁度和开发效率。
- Page Object:将页面元素和操作封装成类,实现“页面与测试用例分离”;
- Page Factory:是PO模式的升级版,用注解简化元素定位,提升代码简洁度和开发效率。
Page Factory ≈ Page Object 模式 + 注解定位 + 元素自动初始化
核心:用@FindBy注解替代手动By定位,自动初始化元素,代码更简洁。
// 1. 页面类:DouBanLoginPageFactory.java(注解定位,复用豆瓣登录代码)
public class DouBanLoginPageFactory {
private WebDriver driver;
private WebDriverWait wait;
// 注解定位(替代By语句,简洁高效,复用豆瓣登录元素)
@FindBy(xpath = "//*[@id=\"db-global-nav\"]/div/div[1]/a")
private WebElement loginBtn; // 首页登录按钮
@FindBy(className = "global-phone-input-phone")
private WebElement phoneInput; // 手机号输入框
@FindBy(className = "account-form-input")
private WebElement codeInput; // 验证码输入框
@FindBy(css = ".btn.btn-phone.btn-active")
private WebElement submitBtn; // 登录提交按钮(CSS注解,解决多class问题)
// 构造方法:初始化驱动和元素(必须写PageFactory.initElements)
public DouBanLoginPageFactory(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
PageFactory.initElements(driver, this); // 自动初始化所有注解元素
}
// 操作方法(直接使用注解元素,无需findElement,对应原代码逻辑)
public void openDouBan(String url) {
driver.get(url);
driver.manage().window().maximize();
}
// 完整登录流程(组合操作,简化调用)
public void login(String phone, String code,String url) throws InterruptedException {
openDouBan(url);
wait.until(ExpectedConditions.visibilityOf(loginBtn)).click(); // 等待登录按钮显示并点击
wait.until(ExpectedConditions.visibilityOf(phoneInput)).sendKeys(phone); // 输入手机号
codeInput.sendKeys(code); // 输入验证码
submitBtn.click(); // 点击提交登录
Thread.sleep(3000);
}
}// 2. 测试用例类:DouBanLoginFactoryTest.java(与PO模式用法一致)
public class DouBanLoginFactoryTest {
public static void main(String[] args) throws InterruptedException {
// 配置驱动(复用你提供的驱动路径)
System.setProperty("webdriver.chrome.driver", "D:\\javasoftware\\ChromeDriver\\chromedriver.exe");
WebDriver driver = new ChromeDriver();
// 创建Page Factory对象,调用登录方法
DouBanLoginPageFactory douBanLoginPage = new DouBanLoginPageFactory(driver);
douBanLoginPage.login("18684811161", "1234", "https://movie.douban.com/"); // 替换为实际手机号和验证码
System.out.println("豆瓣登录事件处理演示完成");
driver.quit(); // 关闭浏览器,释放资源
}
}核心总结
- Page Object:解决代码杂乱、难以维护的问题,核心“一页一类”;
- Page Factory:简化PO模式的元素定位,用注解替代手动By语句,企业主流;
- 学习意义:符合企业开发规范,提升代码复用性和维护性,为后续复杂项目打下基础。
