10 Page Object 模型
10.1 概述
在針對一個WEB頁面編寫自動化測試用例時,需要引用頁面中的元素(數據)才能進行操作(動作)並顯示出頁面內容。如果編寫的測試用例是直接針對WEB頁面元素進行操作,則無法應對經常發生變化的WEB頁面,增加日後自動化代碼的維護成本。而Page Object模型就是針對WEB頁面和元素細節的封裝,並對外提供應用級別的API,從而擺脫對WEB頁面的高耦合情況。示意圖如下所示:
針對以上示例,可以大概總結出大概做法,如下所示:
- 以頁面爲單位,獨立建立模型
- 隱藏實現細節
- 本質是面向接口編程
10.2 定義
Page Object模型(簡稱爲PO模式)是一種設計模式,其核心是分層,實現松耦合,從而實現代碼複用和其易維護性。利用PO模型,爲每個網頁建立兩個類:
- Page類
將每個頁面封裝爲Page類,頁面元素爲Page類成員變量,頁面功能爲Page類方法裏面
- Test類
針對Page類定義的測試類,在測試類中調用Page類中方法完成測試。其使用Page類中的方法與頁面UI元素進行交互操作。若UI發生變化,僅需要更新Page類,測試類無需要更改。
10.3 爲什麼使用Page Object模式
WEB由各種WEB元素(文本框、複選框、多選/單選按鈕等)組成。測試代碼與這些元素進行交互,如果不能正確管理定位器,則代碼的複雜性將成倍增加。當測試代碼和定位器的重複使用,將降低代碼的可讀性,從而進一步加大測試代碼的維護成本。 隨着項目和需求的不斷變化,開發和測試代碼的複雜性會不斷增加,維護性也隨之增加。因此,需要一種方法來解決這種問題,所以我們需要使用PO來嘗試解決這一類問題。
10.4 Page Object模型優點
主要優點如下所示:
- 提高代碼可複用性
不同PO類中的Pabe Object方法可以在不同的測試用例中複用,極大提高代碼的複用性。
- 提高代碼可維護性
因測試場景和定位器是代碼分開,使代碼更加清晰,極大提高代碼的可維護性。
- 減少UI對用例造成的影響
儘管UI經常發生變更,也僅需要修改少量代碼來應對更改,從而減少其帶來的影響。
10.5 Page Object示例
10.5.1 演示環境搭建
我們以官方提供的示例爲演示,操作步驟如下所示:
- 1、訪問官方網址,其網址:https://docs.cypress.io/examples/examples/recipes#Testing-the-DOM
- 2、點擊HTML Web Forms跳轉到Github創建,下載源碼
- 3、將代碼放置到指定目錄並在當前目錄打開終端,執行以下命令,以啓動服務
npm install minimist morgan body-parser express-session express hbs --save-dev
npm start server.js
- 4、在瀏覽器中訪問
默認正確的用戶名和密碼,在server.js中,可以自行修改,如下所示:
10.5.2 演示代碼
本代碼僅僅是演示在Cypress中的Page Object模式(注意與Selenium的區別),主要示例代碼如下所示:
- 1、新建定位器文件loginPageLoctor.json,用於存儲元素定位器
{
"loginPage":{
"username":"input[name=\"username\"]",
"passwd":"input[name=\"password\"]",
"submit":"button[type=\"submit\"]",
"loginFailedPrompt":".error"
}
}
- 2、新建Page類loginPage.js,用於封裝對象和定位元素
/// <reference types="cypress" />
import LoginPageLocator from "./loginPageLoctor.json"
export default class LoginPage{
constructor(visitUrl){
this.url=visitUrl;
}
get username(){
return cy.get(LoginPageLocator.loginPage.username);
}
get passwd(){
return cy.get(LoginPageLocator.loginPage.passwd);
}
get submit(){
return cy.get(LoginPageLocator.loginPage.submit);
}
get errorPrompt(){
return cy.get(LoginPageLocator.loginPage.loginFailedPrompt);
}
get successUrl(){
return cy.url();
}
visit(){
cy.visit(this.url);
}
login(name,pwd) {
if ( name !="" && pwd !=""){
this.username.type(name);
}
if(pwd!=""){
this.passwd.type(pwd);
}
this.submit.click();
}
}
- 3、新建數據文件loginData.json,用於存儲登錄的數據和數據驅動
{
"success": [
{
"caseTitle": "正確的用戶名和密碼,登錄成功",
"user": "jane.lane",
"pwd": "password123",
"checkpoint": "/dashboard"
}
],
"failed": [
{
"caseTitle": "錯誤的用戶名和正確的密碼,登錄失敗",
"user": "Surpass",
"pwd": "password123",
"checkpoint": "Username and/or password is incorrect"
},
{
"caseTitle": "正確的用戶名和錯誤的密碼,登錄失敗",
"user": "jane.lane",
"pwd": "Surpass",
"checkpoint": "Username and/or password is incorrect"
},
{
"caseTitle": "錯誤的用戶名和錯誤的密碼,登錄失敗",
"user": "Surpass",
"pwd": "Surpass",
"checkpoint": "Username and/or password is incorrect"
}
]
}
- 4、新建測試類testLogin.spec.js,測試用例代碼
/// <reference types="cypress" />
import LoginPage from "./loginPage"
import UserData from "./loginData.json"
describe('登錄測試', () => {
let baseUrl = "http://localhost:7077/login";
let login = new LoginPage(baseUrl);
beforeEach(() => {
login.visit(baseUrl);
});
UserData.success.forEach((item)=>{
it(item.caseTitle, () => {
login.login(item.user,item.pwd);
login.successUrl.should("contain",item.checkpoint)
});
});
UserData.failed.forEach((item)=>{
it(item.caseTitle, () => {
login.login(item.user,item.pwd);
login.errorPrompt.should("contain",item.checkpoint)
});
});
});
最終的運行結果如下所示:
原文地址:https://www.jianshu.com/p/f6749544d6f8
本文同步在微信訂閱號上發佈,如各位小夥伴們喜歡我的文章,也可以關注我的微信訂閱號:woaitest,或掃描下面的二維碼添加關注: