PO的思想最早是2013年由IT大佬Martin Flower提出的:
martinfowler.com
A page object wraps an HTML page, or fragment, with an application-specific API, allowing you to manipulate page elements for testing without digging around in the HTML.
没错,就是他
— 没错,就是他 —
在他的文章里有这样一张经典样图,图片中展示了测试代码中直接操作HTML元素和使用PO模式将page对象封装成一个HTML页面,通过特定方法来操作元素的对比;如下图:
我们知道,PO主要就是应用在UI自动化测试上(Web端和App端均适用),因此2015年,Selenium官方给出了PO的设计原则说明:https://github.com/SeleniumHQ/selenium/wiki/PageObjects
对官方的原则进行解读,我们可以得到如下的信息:
不要返回null或者写一个void没有返回值的方法,这样的方法没有意义,既不能为下一步操作创造条件,也不能为用例的断言提供结果。
1.3.3 PO的优点
说的再多,不如动手,下面以QQ邮箱登录为例,演示PO模式在UI自动化中的应用
2.1 登录场景预设
登录页面提供login功能——LoginPage类+login方法
登录页面内有多少元素并不关心,隐藏内部细节
登录成功和失败会返回不同的页面
loginSuccess——MainPage(进入主页面)
loginFail——LoginPage(停留在登录页)
通过方法返回值判断登录是否符合预期
1)创建基础类BasePage,初始化driver,并封装常用的元素操作方法,如click、sendKeys等
package poshow.page;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import java.util.List;
public class BasePage {
public static WebDriver driver;
public WebElement findElement(By by){
return driver.findElement(by);
}
public List finElements(By by){
return driver.findElements(by);
}
public void click(By by){
findElement(by).click();
}
public void sendKeys(By by,String context){
findElement(by).sendKeys(context);
}
public String getText(By by){
return findElement(by).getText();
}
}
2)创建MainPage类,用于登录成功后的返回页面,由于这里并未演示登录后的操作,所以类中无具体方法实现,仅作为loginSuccess后的返回对象
package poshow.page;
public class MainPage extends BasePage{
}
3)创建LoginPage类,继承BasePage类。定义所需元素定位方式并根据操作动作(输入账号、输入密码、点击登录)将其封装成具体的业务操作方法,例如登录成功,用户名错误登录、密码错误登录等,输入的测试数据作为方法的入参传入(username,password)
package poshow.page;
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeDriver;
import java.util.concurrent.TimeUnit;
public class LoginPage extends BasePage{
//定位器
By usernameInput = By.name("u"); //获取用户名输入框
By passwordInput = By.id("p"); //获取密码输入框
By submitLogin = By.cssSelector("#login_button"); //获取登录按钮
By ErrM = By.id("err_m"); //获取错误提示信息
public void openUrl(){
String url = "https://mail.qq.com/";
driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
driver.get(url);
driver.manage().window().maximize();
driver.switchTo().frame("login_frame");
}
private void sleepWait(){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//业务方法
/*
登录方法
*/
private void login(String username,String password){
findElement(usernameInput).clear();
findElement(passwordInput).clear();
sendKeys(usernameInput,username);
sendKeys(passwordInput,password);
click(submitLogin);
}
/*
成功登录
*/
public MainPage loginSuccess(String username,String password){
login(username,password);
return new MainPage();
}
/*
密码错误登录
message:你输入的帐号或密码不正确,请重新输入。
*/
public String loginWithErrPassword(String username,String password ){
login(username,password);
sleepWait();
return getText(ErrM);
}
/*
账号为空登录
你还没有输入帐号!
*/
public String loginWithErrUsername(String username,String password){
login(username,password);
sleepWait();
return getText(ErrM);
}
/*
密码为空登录
*/
public String loginWithoutPassword(String username,String password){
login(username,password);
sleepWait();
return getText(ErrM);
}
}
4)最后创建LoginTest测试类,编写测试用例;用例的编写更接近于人的行为,人想要登录邮箱,只需要依靠用户名和密码完成登录的行为即可,无需关注具体的输入框和登录按钮是如何定位,如何进行输入点击的。并在用例中加入断言进行判断。
package poshow.testcase;
import org.junit.jupiter.api.*;
import poshow.page.LoginPage;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class LoginTest {
LoginPage loginPage = new LoginPage();
@BeforeAll
static void openUrl(){
new LoginPage().openUrl();
}
@Test
@DisplayName("密码错误登录")
@Order(1)
void loginWithErrPassword(){
String username = "376057520";
String password = "123456";
String expectedErrM = "你输入的帐号或密码不正确,请重新输入。";
String errM = loginPage.loginWithErrPassword(username, password);
assertThat(errM,equalTo(expectedErrM));
}
@Test
@DisplayName("账号错误登录")
@Order(2)
void loginWithErrUsername(){
String username = "111";
String password = "123456";
String expectedErrM = "请输入正确的帐号!";
String errM = loginPage.loginWithErrUsername(username, password);
assertThat(errM,equalTo(expectedErrM));
}
@Test
@DisplayName("空密码登录")
@Order(3)
void loginWithoutPassword(){
String username = "376057520";
String password = "";
String expectedErrM = "你还没有输入密码!";
String errM = loginPage.loginWithoutPassword(username, password);
assertThat(errM,equalTo(expectedErrM));
}
@Test
@DisplayName("正确登录")
@Order(4)
void logSuccess(){
String username = "376057520";
String password = "xxx";
loginPage.loginSuccess(username,password);
}
}
5)整体结构展示: