文章目录
- [1. page object 模式简介](#1. page object 模式简介)
-
- [1.1 传统 UI 自动化的问题](#1.1 传统 UI 自动化的问题)
- [1.2 PO 模式的优势](#1.2 PO 模式的优势)
- [2. page object 设计原则](#2. page object 设计原则)
-
- [2.1 PO模式核心思想:](#2.1 PO模式核心思想:)
- [2.2 字段意义](#2.2 字段意义)
- [2.3 方法意义](#2.3 方法意义)
- [2.4 PO模式 使用方法](#2.4 PO模式 使用方法)
- 总结
✨✨✨学习的道路很枯燥,希望我们能并肩走下来!
编程真是一件很奇妙的东西。你只是浅尝辄止,那么只会觉得枯燥乏味,像对待任务似的应付它。但你如果深入探索,就会发现其中的奇妙,了解许多所不知道的原理。知识的力量让你沉醉,甘愿深陷其中并发现宝藏。

本文开始
1. page object 模式简介
PO模式本质:将冗余的测试过程进行分层处理;
Page Object模式:是将每个页面封装成一个对象,页面元素作为对象属性,页面操作作为对象方法。使用Java的面向对象特性,通过类来组织页面元素和操作。
下面是伪代码示例:
元素定位层:
java
// ElementLocators.java - 专门存放元素定位器
public class LoginPageLocators {
// 所有定位器作为公共静态常量
public static final By USERNAME_INPUT = By.id("username");
public static final By PASSWORD_INPUT = By.id("password");
public static final By LOGIN_BUTTON = By.id("login-btn");
public static final By ERROR_MESSAGE = By.className("error-msg");
public static final By REMEMBER_ME_CHECKBOX = By.name("remember");
// 动态定位器(如果需要)
public static By dynamicLink(String linkText) {
return By.linkText(linkText);
}
}
页面操作层:
java
// LoginPage.java - 专门处理页面操作
public class LoginPage {
private final WebDriver driver;
public LoginPage(WebDriver driver) {
this.driver = driver;
}
// 业务操作方法
public HomePage login(String username, String password) {
enterUsername(username);
enterPassword(password);
clickLogin();
return new HomePage(driver);
}
//登录失败
public void loginWithError(String username, String password) {
enterUsername(username);
enterPassword(password);
clickLogin();
// 停留在当前页面,因为登录失败
}
// 原子操作方法
//输入账号方法
public void enterUsername(String username) {
WebElement element = driver.findElement(LoginPageLocators.USERNAME_INPUT);
element.clear();
element.sendKeys(username);
}
//输入密码方法
public void enterPassword(String password) {
driver.findElement(LoginPageLocators.PASSWORD_INPUT)
.sendKeys(password);
}
//点击登录方法
public void clickLogin() {
driver.findElement(LoginPageLocators.LOGIN_BUTTON)
.click();
}
//获取错误信息
public String getErrorMessage() {
return driver.findElement(LoginPageLocators.ERROR_MESSAGE)
.getText();
}
// 支持链式调用
public LoginPage withUsername(String username) {
enterUsername(username);
return this;
}
public LoginPage withPassword(String password) {
enterPassword(password);
return this;
}
public HomePage andLogin() {
clickLogin();
return new HomePage(driver);
}
}
测试用例层伪代码:
java
// LoginTests.java - 专门写测试用例
public class LoginTests {
private WebDriver driver;
private LoginPage loginPage;
@BeforeEach
public void setup() {
driver = new ChromeDriver();
loginPage = new LoginPage(driver);
driver.get("https://example.com/login");
}
@AfterEach
public void teardown() {
if (driver != null) {
driver.quit();
}
}
@Test
@DisplayName("用户使用正确凭据可以登录")
public void userCanLoginWithValidCredentials() {
// 使用业务方法
HomePage homePage = loginPage.login("admin", "password123");
// 验证
assertTrue(homePage.isUserLoggedIn());
assertEquals("Dashboard", homePage.getPageTitle());
}
@Test
@DisplayName("用户使用错误密码登录失败")
public void userCannotLoginWithInvalidPassword() {
// 使用链式调用
loginPage.withUsername("admin")
.withPassword("wrong")
.loginWithError();
// 验证错误信息
String errorMsg = loginPage.getErrorMessage();
assertEquals("Invalid password", errorMsg);
}
}
1.1 传统 UI 自动化的问题
传统UI自动化测试脚本: 直接将测试逻辑、元素定位、数据验证混合在一起,导致:
1)代码重复:相同元素在多处重复定位,无法适应 UI 频繁变化;
如:当一个元素修改,使用该元素的多个重复地方都需要修改;
2)可读性差:业务逻辑和实现细节混杂,无法清晰表达业务用例场景;
解释:不易看出具体操作逻辑;
3)维护困难:页面变化需要修改多处代码,存在大量的样板代码 driver/find/click
4)复用性低:无法在不同测试中复用页面操作
传统自动化测试登录样例:
java
/***
* Tests login feature
*/
public class Login {
public void testLogin() {
// fill login data on sign-in page
driver.findElement(By.name("user_name")).sendKeys("userName");
driver.findElement(By.name("password")).sendKeys("my supersecret password");
driver.findElement(By.name("sign-in")).click();
// verify h1 tag is "Hello userName" after login
driver.findElement(By.tagName("h1")).isDisplayed();
assertThat(driver.findElement(By.tagName("h1")).getText(), is("Hello userName"));
}
}
1.2 PO 模式的优势
PO模式通过封装带来以下核心优势:
1)代码复用:页面操作一次定义,多次使用
2)易于维护:页面变化只需修改对应Page Object
3)可读性强:测试用例反映业务逻辑,而非实现细节
4)团队协作:测试开发分离,并行工作
5)健壮性:统一处理等待、异常等
小结:使用PO模式,降低 UI 变化导致的测试用例脆弱性问题
让用例清晰明朗,与具体实现无关
2. page object 设计原则
2.1 PO模式核心思想:
PO模式:
- 将测试脚本从"如何操作"转变为"要做什么",让测试代码更关注业务逻辑,而非实现细节。
- 当页面UI变化时,只需要修改对应的Page Object类,而不需要修改测试用例,真正实现了关注点分离
2.2 字段意义
- 不要暴露页面内部的元素给外部
解释:页面内部的元素(按钮、输入框)应该封装在页面类内部,外部测试代码不需要知道具体怎么找到这些元素,只需要知道能做什么操作。
java
// ✅ 正确做法:封装起来
public class LoginPage {
private By usernameInput = By.id("username"); // private修饰,不暴露
public void enterUsername(String text) {
// 内部处理,外部不知道具体怎么找元素
driver.findElement(usernameInput).sendKeys(text);
}
}
// 测试代码只调用方法
loginPage.enterUsername("admin"); // 简洁,不关心内部实现
- 不需要建模 UI 内的所有元素
解释:只封装测试需要用到的元素,没用的元素不要写进来。
2.3 方法意义
- 用公共方法代表 UI 所提供的功能
解释:方法就是那些按钮,代表用户能做的操作。
java
// ✅ 正确做法:方法名代表业务功能
public class ShoppingCartPage {
public OrderPage proceedToCheckout() { // 代表"进行结账"这个功能
driver.findElement(By.id("checkout")).click();
return new OrderPage(driver); // 返回下一页
}
public ShoppingCartPage updateQuantity(String itemId, int quantity) {
// 代表"更新数量"功能
// ...实现代码
return this; // 返回当前页,支持链式调用
}
}
// 使用:读起来像自然语言
cartPage.proceedToCheckout(); // 进行结账
cartPage.updateQuantity("item1", 3); // 更新数量
- 方法应该返回其他的 PageObject 或者返回用于断言的数据。
解释:方法要么带你去下一个页面(返回新页面对象),要么给你一些信息让你检查(返回数据),不要什么都不给 - 同样的行为不同的结果可以建模为不同的方法
解释:同样是"登录"这个行为,成功和失败是不同的场景,应该用不同的方法处理,就像"正常登录"和"忘记密码登录"是两回事。
java
// ✅ 正确做法:不同结果用不同方法
public class LoginPage {
// 成功登录:返回下一页
public HomePage loginSuccessfully(String user, String pass) {
enterCredentials(user, pass);
clickLogin();
return new HomePage(driver); // 假设总是成功
}
// 登录失败:停留在当前页,可以检查错误
public LoginPage loginWithInvalidCreds(String user, String pass) {
enterCredentials(user, pass);
clickLogin();
return this; // 返回自己,因为还在登录页
}
}
- 不要在方法内加断言
解释:页面对象的方法只管"做事情",不要管"检查对错"。断言应该放在测试代码里。
2.4 PO模式 使用方法
- 把元素信息和操作细节封装到 PageObject 类中
- 根据业务逻辑,在测试用例中链式调用
PO模式中测试用例代码示例:
java
public class OptimizedLoginTests {
private WebDriver driver;
private PageFactory pages;
@BeforeEach
public void setup() {
driver = WebDriverManager.chromedriver().create();
driver.manage().window().maximize();
pages = new PageFactory(driver);
// 导航到登录页
driver.get("https://example.com");
pages.loginPage().isLoginPageDisplayed(); // 等待页面加载
}
@Test
@DisplayName("端到端登录流程测试")
public void endToEndLoginFlow() {
// 使用页面工厂
LoginPage loginPage = pages.loginPage();
// 执行登录
HomePage homePage = loginPage.performLogin("testuser", "SecurePass123!");
// 验证登录成功
assertTrue(homePage.isUserMenuVisible(), "用户菜单应该可见");
assertTrue(homePage.getWelcomeMessage().contains("testuser"),
"欢迎消息应包含用户名");
// 验证导航
DashboardPage dashboard = homePage.navigateToDashboard();
assertTrue(dashboard.isDashboardLoaded(), "仪表板应加载成功");
}
@Test
@DisplayName("数据驱动登录测试")
@CsvSource({
"admin, admin123, true",
"user, wrongpass, false",
"'', password, false",
"admin, '', false"
})
public void dataDrivenLoginTest(String username, String password, boolean shouldSucceed) {
LoginPage loginPage = pages.loginPage();
loginPage.performLogin(username, password);
LoginPage.LoginState state = loginPage.getLoginState();
if (shouldSucceed) {
assertEquals(LoginPage.LoginState.LOGGED_IN, state);
} else {
assertNotEquals(LoginPage.LoginState.LOGGED_IN, state);
}
}
}
总结
✨✨✨各位读友,本篇分享到内容是否更好的帮助你理解,如果对你有帮助给个👍赞鼓励一下吧!!
🎉🎉🎉世上没有绝望的处境,只有对处境绝望的人。
🎉🎉🎉一遇挫折就灰心丧气的人,永远是个失败者。而一向努力奋斗,坚韧不拔的人会走向成功。
感谢每一位一起走到这的伙伴,我们可以一起交流进步!!!一起加油吧!!!
