Cypress系列(62)- 改造 PageObject 模式


如果想從頭學起Cypress,可以看下面的系列文章哦

https://www.cnblogs.com/poloyy/category/1768839.html

 

PO 模式

PageObject(頁面對象)模式是自動化測試中的一個最佳實踐,相信很多小伙伴都知道的

 

PO 模式特征

  • 將每個頁面(或者待測試對象)封裝成一個(class),類里面包含了頁面上所有元素及它們的操作方法(單步操作或功能集合)
  • 測試代碼和被測頁面代碼解耦,使用 PO 模式后,當頁面發生改變,無須改變測試代碼,僅改頁面代碼

 

接下來就講解下 Cypress 下如何使用 PO 模式

 

前期准備

啟動 Cypress 提供的演示項目

cmd 窗口進入下面的文件夾

 

執行下面的命令

npm start

 

PO 模式代碼

簡單的 PageObject 模型栗子

待測試頁面代碼

在 C:\Users\user\Desktop\py\cypress-example-recipes\examples\logging-in__html-web-forms\cypress 文件夾下新建 pages 文件夾,並創建一個 login.js 待測試頁面文件,代碼如下

// login.js
export default class LoginPage {
    constructor() {

        this.userName = 'input[name=username]'
        this.password = 'input[name=password]'
        this.form = 'form'
        this.url = 'http://localhost:7077/login'
    }

    isTargetPage() {
        cy.visit('/login')
        cy.url().should('eq', this.url)
    }

    login(username, pwd) {
        cy.get(this.userName).type(username)
        cy.get(this.password).type(pwd)
        cy.get(this.form).submit()
    }

}

 

測試用例文件

在 C:\Users\user\Desktop\py\cypress-example-recipes\examples\logging-in__html-web-forms\cypress\integration 文件夾下,創建一個 testLogin.js 測試用例文件,代碼如下

import LoginPage from "../pages/login"


context('登錄測試,PO 模式', function () {

    const username = 'jane.lane'
    const pwd = 'password123'

    it('登錄成功', function () {
        // 創建 po 實例
        const loginInstance = new LoginPage()
        loginInstance.isTargetPage()
        loginInstance.login(username, pwd)
        cy.url().should('include', '/dashboard')
    });

})

 

測試結果

 

總結下

這樣的 PageObject 模式代碼只是把定位元素的元素定位表達式給剝離出來,並沒有針對元素本身進行封裝

 

針對元素本身進行封裝的栗子

待測試頁面代碼

// login.js

export default class LoginPage {
    constructor() {
        this.userNameLocator = 'input[name=username]'
        this.passwordLocator = 'input[name=password]'
        this.formLocator = 'form'
        this.url = 'http://localhost:7077/login'
    }

    get username() {
        return cy.get(this.userNameLocator)
    }

    get password() {
        return cy.get(this.passwordLocator)
    }

    get form() {
        return cy.get(this.formLocator)
    }

    isTargetPage() {
        cy.visit('/login')
        cy.url().should('eq', this.url)
    }

    login(username, pwd) {
        this.username.type(username)
        this.password.type(pwd)
        this.form.submit()
    }

}

 

跳轉頁面代碼

當登錄成功后,頁面將跳轉至 mainPage 頁面,上面只寫了 login 頁面,這里寫下跳轉后的頁面

// login.js

export default class mainPage{
    constructor() {
        this.h1Locator = 'h1'
        this.url = 'http://localhost:7077/dashboard'
    }

    get welComeText() {
        return cy.get(this.h1Locator)
    }

    isTargetPage() {
        cy.url().should('eq', this.url)
    }
}

 

測試用例代碼

context('登錄測試,PO 模式', function () {

    const username = 'jane.lane'
    const pwd = 'password123'

    it('登錄成功', function () {
        // 創建 po 實例
        const loginInstance = new LoginPage()
        loginInstance.visitPage()
        loginInstance.isTargetPage()
        cy.login(username, pwd)
        cy.url().should('include', '/dashboard')

        const manInstance = new mainPage()
        manInstance.isTargetPage()
        manInstance.welComeText.should('contain', 'jane.lane')
    });
})

 

測試結果

 

總結下

  •  login.js 和 mainPage.js 兩個頁面對象都有一個 isTargetPage() 函數來判斷當前頁面 URL 是否正確
  • 那這里就將每個 page 都共用的部分再次剝離,放到一個新的 common page
  • 然后每個 page 都繼承自 common page(類似 selenium po 模式的 BasePage)

 

使用 common page 的栗子

commonPage.js 的代碼

它也在 pages 文件夾下創建

export default class commanPage {
    constructor() {
        // 構造函數可以為空
        // 如果不為空 應該是所有 page 都會用到的變量
    }

    isTargetPage() {
        cy.url().should('eq', this.url)
    }
}

 

login.js 的代碼

// login.js
import commanPage from "./commonPage";

// 繼承 commonPage
export default class LoginPage extends commanPage{
    constructor() {
        // 調用父類的構造方法
        super()
        this.userNameLocator = 'input[name=username]'
        this.passwordLocator = 'input[name=password]'
        this.formLocator = 'form'
        this.url = 'http://localhost:7077/login'
    }

    get username() {
        return cy.get(this.userNameLocator)
    }

    get password() {
        return cy.get(this.passwordLocator)
    }

    get form() {
        return cy.get(this.formLocator)
    }

    visitPage(){
        cy.visit('/login')
    }

    login(username, pwd) {
        this.username.type(username)
        this.password.type(pwd)
        this.form.submit()
    }

}

 

mainPage.js 的代碼

// login.js
import commonPage from "./commonPage";

// 繼承 commonPage
export default class mainPage extends commonPage {
    constructor() {
        super()
        this.h1Locator = 'h1'
        this.url = 'http://localhost:7077/dashboard'
    }

    get welComeText() {
        return cy.get(this.h1Locator)
    }
}

 

測試結果

測試結果和上面的栗子一樣

 

Cypress 使用 PO 模式的總結

  • Cypress 完全支持 PageObject 模式
  • 但存在一個問題,如果一個測試需要訪問多個頁面對象,就意味着測試中要初始化多個頁面對象實例(new Page())
  • 如果一個頁面對象需要登錄才能訪問(大部分場景都是這樣),則每次初始化都需要先登錄再訪問(只有登錄后才能重用 cookie),這無形增加了測試運行的時間
  • Cypress 不認為 PO 模式是一個好模式,它認為跨頁面共享邏輯是一個反邏輯,因為 Cypress 的實現原理與其他工具完全不同
  • 那 Cypress 用什么方式來替代 PO 模式呢?答案就是下一篇要講到的 Custom Commons


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM