你不知道的Cypress系列(2) -- ”該死"的PO模型?!
iTesting,愛(ài)測(cè)試,愛(ài)分享
我的新書(shū)《前端自動(dòng)化測(cè)試框架Cypress從入門(mén)到精通》出版啦!
自從我的新書(shū)<前端自動(dòng)化測(cè)試框架 -- Cypress從入門(mén)到精通>上市以來(lái),這本書(shū)受到了大量同學(xué)熱情的追捧和討論。在跟同學(xué)們的交流中,我也了解到, 原來(lái)除了國(guó)外優(yōu)秀的公司(例如Adobe, 迪士尼,AutoDesk等等), 國(guó)內(nèi)也有很多公司在嘗試使用Cypress提升測(cè)試效率。而在Cypress中國(guó)群內(nèi)、在公眾號(hào)iTesting里,我每天都能看到大量關(guān)于Cypress的使用討論和私下問(wèn)詢。這讓我感到無(wú)比榮幸。(買(mǎi)了書(shū)的同學(xué)們,公眾號(hào)回復(fù)你的微信號(hào),拉你到Cypress中國(guó)群)。
除了日常推薦大家通過(guò)閱讀我的書(shū)來(lái)解決日常Cypress使用問(wèn)題外,我也一直在更新著我這邊的Cypress知識(shí)圖譜, 不夸張的說(shuō),目前我總結(jié)和實(shí)踐下來(lái)知識(shí)點(diǎn)多達(dá)200多篇。本著“雕琢自我,普惠他人”的原則,我決定在公眾號(hào)iTesting上開(kāi)設(shè)<你不知道的Cypress系列>專(zhuān)欄。此專(zhuān)欄目的是分享一些我自己趟過(guò)的坑,走過(guò)的彎路、以及在選型時(shí)拋棄了的實(shí)踐。希望讓大家在選用Cypress作為前端自動(dòng)化測(cè)試框架方案時(shí), 可以借鑒一下,避免再走我走過(guò)的彎路。
今天是<你不知道的Cypress系列>的第二篇 -- 絕知此事要躬行,別被Cypress官方忽悠瘸了!
為了讓大家看到標(biāo)題就知道我再說(shuō)什么,我把標(biāo)題更改為:
你不知道的Cypress系列(2) -- ”該死"的PO模型
01
—
PO模型是什么
無(wú)論你基于何種自動(dòng)化測(cè)試框架開(kāi)發(fā)你的測(cè)試腳本,PO模型絕對(duì)是你繞不過(guò)的坎兒。
PO模型(Page Object Module)算得上自動(dòng)化測(cè)試的最佳實(shí)踐之一,其中心思想如下:
把物理上的頁(yè)面或者邏輯上的功能組合當(dāng)成一個(gè)Page 類(lèi)處理。
針對(duì)每一個(gè)Page類(lèi),將此Page上所屬的元素、此Page類(lèi)上元素動(dòng)作的組合分別封裝成Object, 以及Class Methods。
所有針對(duì)此頁(yè)面的操作以Page 類(lèi)的實(shí)例引用。
從代碼實(shí)現(xiàn)上來(lái)看,元素、元素操作、 Page類(lèi)、Page類(lèi)對(duì)應(yīng)的測(cè)試類(lèi)就是PO。
實(shí)現(xiàn)PO模型后,測(cè)試用例的操作細(xì)節(jié)會(huì)被隱藏,轉(zhuǎn)而以面向?qū)ο?,或者說(shuō),以業(yè)務(wù)角度展示操作步驟,我們直接看一個(gè)PO封裝后的測(cè)試用例:
import LoginPage from "../pages/login"import mainPage from "../pages/main"describe('PageObject模式之登錄測(cè)試', function () {// 關(guān)注公眾號(hào)iTesting,玩轉(zhuǎn)Cypressconst username = 'iTesting'const password = 'password123'it('登錄成功', function () {const loginInstance = new LoginPage()loginInstance.visitPage().isTargetPage().login(username, password)const mainInstance = new mainPage()mainInstance.isTargetPage().welComeText.should('contain', 'iTesting')})})
從業(yè)務(wù)角度看,PO模型非常直觀:
初始化LoginPage實(shí)例訪問(wèn)LoginPage判斷LoginPage可訪問(wèn)登錄接著訪問(wèn)mainPage(登錄后會(huì)跳轉(zhuǎn)的頁(yè)面)判斷mainPage可訪問(wèn)在mainPage上斷言
02
—
PO模型的好處
由上文可以看到, PO模型的目的,主要是為了重用元素,做到每個(gè)元素定位、每個(gè)元素、甚至每個(gè)類(lèi)方法,在整個(gè)項(xiàng)目中,有且僅有一處定義,其它都是調(diào)用。通過(guò)這樣的方式,PO模型做到了即使在復(fù)雜項(xiàng)目中,也不會(huì)增加維護(hù)成本。
除此之外,在當(dāng)前微服務(wù)開(kāi)發(fā)模式下,動(dòng)輒十幾個(gè)、幾十個(gè)微服務(wù)會(huì)同步進(jìn)行開(kāi)發(fā)。那么,過(guò)去那種一個(gè)測(cè)試工程師搞定所有自動(dòng)化測(cè)試的機(jī)會(huì)不再有了。當(dāng)前大多數(shù)公司的實(shí)踐是將測(cè)試框架收歸專(zhuān)門(mén)團(tuán)隊(duì)負(fù)責(zé),而將自動(dòng)化腳本的編寫(xiě)下放到各微服務(wù)團(tuán)隊(duì)。對(duì)應(yīng)的, 各個(gè)團(tuán)隊(duì)下的業(yè)務(wù)測(cè)試工程師要具體負(fù)責(zé)其微服務(wù)的自動(dòng)化測(cè)試。
PO模型天然帶來(lái)一個(gè)好處,即,Page類(lèi)天然隔離了模塊和團(tuán)隊(duì)。例如我是團(tuán)隊(duì)A的測(cè)試工程師,除去公用Page外,我只需要關(guān)注我這個(gè)微服務(wù)下的所有Page類(lèi)及類(lèi)方法即可。而不必關(guān)心其它團(tuán)隊(duì)所own的頁(yè)面。
如果我對(duì)其它組的服務(wù)有依賴,這些通常會(huì)構(gòu)建專(zhuān)門(mén)的函數(shù)并成為Common Page的一部分。
如果有新人進(jìn)來(lái),他的學(xué)習(xí)成本只是我們團(tuán)隊(duì)負(fù)責(zé)的頁(yè)面 + 公用Page,相對(duì)來(lái)說(shuō)比較友好。
03
—
Cypress怎么看PO模型
正如Cypress官方所宣揚(yáng)的一樣:
// The page object pattern isn’t actually anything “special”.
換句話說(shuō), Page Object只是方便重用而已,沒(méi)什么大不了。
Cypress官方覺(jué)得Page Object模型里的大量Page類(lèi)及其對(duì)應(yīng)的測(cè)試類(lèi)的使用,會(huì)加重調(diào)用鏈條,隱藏各個(gè)操作之間的動(dòng)作細(xì)節(jié),加重使用者的負(fù)擔(dān), 具體來(lái)說(shuō):
使用PO模型人為的在測(cè)試中引入了其他狀態(tài),這些狀態(tài)是你(測(cè)試腳本創(chuàng)建者)自己定義的,而不是應(yīng)用程序內(nèi)部擁有的, 它增加了debug成本。
// 這個(gè)假設(shè)是成立的,因?yàn)槌绦騼?nèi)部并沒(méi)有Page,更遑論P(yáng)age里的方法。// 那么當(dāng)你運(yùn)行失敗時(shí)發(fā)現(xiàn),Page.addWallet失敗了,你無(wú)法直接知道哪里出錯(cuò)// 你必須找到addWallet的定義,再去查看其實(shí)現(xiàn),才能知道哪里錯(cuò)。
2. 使用PO模型使得測(cè)試速度變慢。
// 這也是事實(shí)。// 畢竟你每次操作都要先initial Page實(shí)例,然后再尋找類(lèi)方法,最后才是執(zhí)行。
3. 使用PO模型使代碼陷入“Conditional Testing”的怪圈。
// 比如你的方法里存在如下判斷:// 如果頁(yè)面發(fā)生AAA, 你會(huì)進(jìn)行BBB操作, 如果發(fā)生CCC,你會(huì)進(jìn)行DDD操作。// 這在Cypress看來(lái)是反模式。因?yàn)镃ypress跟你的應(yīng)用程序運(yùn)行在同一個(gè)生命周期。// Cypress可以捕獲應(yīng)用程序里發(fā)生的一切。// 所以,你理應(yīng)知道你的操作引發(fā)的結(jié)果到底是AAA還是CCC。
這3條, 條條劍指PO模型的七寸。
Cypress官方又說(shuō),好既然PO不好用, 而且它存在只是為了方便重用,那么我給你更好的辦法:
于是Custom Commands出爐了。Custom Commands你可以看成是PO模型里的Common Page。所有在Custom Commands里定義的方法,天生可以被任何測(cè)試之間調(diào)用。相當(dāng)于你生成了自己的全局命令。
來(lái)看一個(gè)Custom Commands的具體例子:
Cypress.Commands.add('login', (username, password) => {Cypress.log({name: 'login',message: `${username} | ${password}`,})// 關(guān)注公眾號(hào)iTesting,精通Cypress使用return cy.request({method: 'POST',url: '/login',form: true,body: {username,password,},})})
這樣,在使用上,你也不需要管什么Login Page類(lèi),Login測(cè)試類(lèi)了。你在任何代碼里直接寫(xiě):
cy.login("關(guān)注iTesting,玩轉(zhuǎn)Cypress")
它自然幫你登錄成功, Page是什么?頓時(shí)不香了好嗎!
Custom Commands的具體用法我的新書(shū)<前端自動(dòng)化測(cè)試框架 -- Cypress從入門(mén)到精通>里講的還算通透,這里就不多說(shuō)。
另外,在JavaScript世界里, 很講究一個(gè)鏈?zhǔn)秸{(diào)用(Chainable), Custom COmmands + 鏈?zhǔn)秸{(diào)用,Cypress認(rèn)為它完全可以取代PO模型。
通過(guò)chainable把你的所有操作“可視化”。于是,一個(gè)Cypress推崇的測(cè)試用例就變成這樣:
///describe('Custom Commands模式之登錄測(cè)試', function () {// 關(guān)注公眾號(hào)iTesting,玩轉(zhuǎn)Cypressit('登錄成功', function () {cy.login(username, password).verifyLoginSuccess().verifyWelcomeTxt()})})
從cypress角度,你看到的是login成功后直接去驗(yàn)證welcome的文本在不在。
這樣感覺(jué)代碼量是不是更少,代碼更直觀了?
03
—
我怎么看PO和Custom Commands
這里我也談下我對(duì)PO和Custom Commands的看法。
首先要謹(jǐn)記:Cypress的出現(xiàn)是為Developer服務(wù)的,它對(duì)Developer的友好程度要高于Tester。
基于此,我們?cè)賮?lái)看登錄成功這個(gè)用例, 你看到的是
cy.xxx().yyy().zzz()
這樣的模式調(diào)用鏈清晰,從層次上來(lái)是,也比PO模型少了一層。出錯(cuò)后的調(diào)試,也更方便。
但是!
你的測(cè)試用例都是cy這樣,cy那樣,當(dāng)然對(duì)于Cypress官方來(lái)說(shuō),很成功,Visibility非常高,簡(jiǎn)直是Cypress的活廣告,美滋滋?。?/span>
可是,你的“業(yè)務(wù)”呢?
不錯(cuò)!你的業(yè)務(wù)以及業(yè)務(wù)細(xì)節(jié)被隱藏了!
雖然從Cypress的Custom Commands方式讓測(cè)試寫(xiě)起代碼來(lái)更爽,但是別忘記,在國(guó)內(nèi),我們還存在大量的測(cè)試人員,測(cè)試開(kāi)發(fā)水平不足?。ù藭r(shí)應(yīng)該有廣告,我的拉勾專(zhuān)欄<測(cè)試開(kāi)發(fā)入門(mén)與實(shí)戰(zhàn)>開(kāi)欄24小時(shí)內(nèi)售賣(mài)超10000+, 破了測(cè)試專(zhuān)欄的記錄,值得你去拉勾上搜一下 :))
而且,從習(xí)慣上來(lái)說(shuō),國(guó)內(nèi)的同學(xué)們更習(xí)慣從業(yè)務(wù)角度去理解測(cè)試。并且Custom Commands把所有的公用功能都寫(xiě)在一個(gè)文件里,對(duì)測(cè)試人員來(lái)說(shuō)不友好,當(dāng)我的測(cè)試用例超過(guò)10000條時(shí),Custom Commands里的公用方法,恐怕也有幾百個(gè)了??偛荒芪襾?lái)一個(gè)新人,讓他花上幾周時(shí)間去熟悉所有的方法吧!況且,都微服務(wù)了,他以后基本也只負(fù)責(zé)其中一些測(cè)試,那么我讓他學(xué)那么多跟他沒(méi)關(guān)系的方法干嘛呢?
雖然Custom Commands也可以做到按照微服務(wù)組織,然后在每個(gè)微服務(wù)Folder下實(shí)現(xiàn)一個(gè)Custom Commands的子域。但是,這樣做,你使用時(shí)候由要跟PO模型一樣先引入再使用, 那么它跟PO模型又有什么區(qū)別呢??。?/span>
04
—
PO和Custom Commands我都要
紙上得來(lái)終覺(jué)淺!
紙上得來(lái)終覺(jué)淺!
紙上得來(lái)終覺(jué)淺!
不能迷信權(quán)威!我剛開(kāi)始搭建公司的前端框架時(shí), 我就完全按照Cypress官方建議做,結(jié)果,當(dāng)我的測(cè)試用例到達(dá)幾千條時(shí),我傻了,Custom Commands里的方法幾百個(gè),即使是我自己寫(xiě)的,但我自己也鬧不清楚哪個(gè)做哪個(gè)用處,因?yàn)闆](méi)有了Page做參考,時(shí)間一長(zhǎng),我很難從函數(shù)命名上看出這個(gè)方法應(yīng)該在那個(gè)頁(yè)面下使用, 更別說(shuō)對(duì)框架不熟悉的新人了。
結(jié)果沒(méi)辦法,我重新返工了一遍,把特別核心的公用功能放到Custom Commands里,把跟業(yè)務(wù)有關(guān)的,還是以Page Object方式組織。放到微服務(wù)下,放到Page下,這樣, 再來(lái)新人,我告訴他,你只要看這個(gè)commands這個(gè)文件還有你的微服務(wù)folder就好了。
所以, PO + Custom Commands + chainable是我的最佳實(shí)踐, 以后我的測(cè)試用例就變成這樣了。
///
import mainPage from "../pages/main"describe('PageObject模式之登錄測(cè)試', function () {// 關(guān)注公眾號(hào)iTesting,玩轉(zhuǎn)Cypressconst username = 'iTesting'const password = 'password123'it('登錄成功', function () {cy.login(username, password)const mainInstance = new mainPage()mainInstance.isTargetPage().welComeText.should('contain', 'iTesting')})})
這下終于清靜了!是么?
Cypress又提出了一個(gè)模型,App Actions, 同學(xué),你想去嘗嘗鮮嗎?
往期回看:
你不知道的Cypress系列(1) --雞肋的BDD
下期預(yù)告:
你不知道的Cypress系列(3) -- 是時(shí)候重構(gòu)自己的思維了!
為了更好的支持我創(chuàng)作,麻煩同學(xué)們動(dòng)動(dòng)小手,點(diǎn)贊 + 在看 + 轉(zhuǎn)發(fā)一鍵三聯(lián):)
技術(shù)討論
公眾號(hào)里直接回復(fù) 666, 帶你入圈。
- - 時(shí)人莫小池中水, 淺處不妨有臥龍 - -
作者:
Kevin Cai, 江湖人稱蔡老師。
兩性情感專(zhuān)家,非著名測(cè)試開(kāi)發(fā)。
技術(shù)路線的堅(jiān)定支持者,始終相信Nobody can be somebody。
· 猜你喜歡的文章 ·
