摘要:本篇內(nèi)容將記錄并介紹使用進(jìn)行自動(dòng)化網(wǎng)頁(yè)測(cè)試,并依靠約定來(lái)避免反復(fù)修改測(cè)試用例的方案。總結(jié)根據(jù)以上的功能劃分,我們很好的將一整個(gè)應(yīng)用拆分成各個(gè)測(cè)試單元進(jìn)行單元測(cè)試。
本篇內(nèi)容將記錄并介紹使用Puppeteer進(jìn)行自動(dòng)化網(wǎng)頁(yè)測(cè)試,并依靠約定來(lái)避免反復(fù)修改測(cè)試用例的方案。主要解決頁(yè)面眾多時(shí),修改代碼導(dǎo)致的牽連錯(cuò)誤無(wú)法被發(fā)現(xiàn)的運(yùn)行時(shí)問(wèn)題。文章首發(fā)于個(gè)人博客。起因
對(duì)前端感興趣希望一起討論的可以加我vx:w554091944
目前我們?cè)诔掷m(xù)開(kāi)發(fā)著一個(gè)幾十個(gè)頁(yè)面,十萬(wàn)+行代碼的項(xiàng)目,隨著產(chǎn)品的更迭,總會(huì)出現(xiàn)這樣的問(wèn)題。在對(duì)某些業(yè)務(wù)邏輯或者功能進(jìn)行添加或者修改的時(shí)候(尤其是通用邏輯),這些通用的邏輯或者組件往往會(huì)牽扯到一些其他地方的問(wèn)題。由于測(cè)試人員受限,我們很難在完成一個(gè)模塊單元后,對(duì)所有功能重新測(cè)試一遍。
同時(shí),由于環(huán)境及數(shù)據(jù)的區(qū)別,(以及在開(kāi)發(fā)過(guò)程中對(duì)代碼完備性的疏忽),代碼會(huì)在某些特殊數(shù)據(jù)的解析和和展示上出現(xiàn)問(wèn)題,在開(kāi)發(fā)和測(cè)試中很難去發(fā)現(xiàn)??偟膩?lái)說(shuō),我們希望有一個(gè)這樣的工具,幫我們解決上述幾個(gè)問(wèn)題:
在進(jìn)行代碼和功能改動(dòng)后,能夠自動(dòng)訪問(wèn)各個(gè)功能的頁(yè)面,檢測(cè)問(wèn)題
針對(duì)大量的數(shù)據(jù)內(nèi)容,進(jìn)行批量訪問(wèn),檢測(cè)對(duì)于不同數(shù)據(jù)的展示是否存在問(wèn)題
測(cè)試與代碼功能盡量不耦合,避免每次上新功能都需要對(duì)測(cè)試用例進(jìn)行修改,維護(hù)成本太大
定期的測(cè)試任務(wù),及時(shí)發(fā)現(xiàn)數(shù)據(jù)平臺(tái)針對(duì)新數(shù)據(jù)的展示完備性
其中,最重要的問(wèn)題,就是將測(cè)試代碼與功能解耦,避免每次迭代和修改都需要追加新的測(cè)試用例。我們?nèi)绾巫龅竭@一點(diǎn)呢?首先我們來(lái)梳理下測(cè)試平臺(tái)的功能。
功能設(shè)定由于我們的平臺(tái)主要是進(jìn)行數(shù)據(jù)展示,所以我們?cè)跍y(cè)試過(guò)程中,主要以日常的展示數(shù)據(jù)為重心即可,針對(duì)一些復(fù)雜的表單操作先不予處理。針對(duì)上述的幾個(gè)問(wèn)題,我們針對(duì)自動(dòng)化測(cè)試工具的功能如下:
依次訪問(wèn)各個(gè)頁(yè)面
訪問(wèn)各個(gè)頁(yè)面的具體內(nèi)容,如時(shí)間切換、選項(xiàng)卡切換、分頁(yè)切換、表格展開(kāi)行等等
針對(duì)數(shù)據(jù)表格中的詳情鏈接,選擇前100條進(jìn)行訪問(wèn),并進(jìn)行下鉆頁(yè)的繼續(xù)測(cè)試
捕獲在頁(yè)面中的錯(cuò)誤請(qǐng)求
對(duì)錯(cuò)誤信息進(jìn)行捕獲,統(tǒng)計(jì)和上報(bào)
根據(jù)以上的梳理,我們可以把整個(gè)應(yīng)用分為幾個(gè)測(cè)試單元
頁(yè)面單元,檢測(cè)各功能頁(yè)面訪問(wèn)的穩(wěn)定性
詳情頁(yè)單元,根據(jù)頁(yè)面的數(shù)據(jù)列表,進(jìn)行批量的詳情頁(yè)跳轉(zhuǎn),檢測(cè)不同參數(shù)下詳情頁(yè)的穩(wěn)定性
功能單元,用于檢測(cè)頁(yè)面和詳情頁(yè)各種展示類型點(diǎn)擊切換后是否產(chǎn)生錯(cuò)誤
通過(guò)這樣的劃分,我們針對(duì)各個(gè)單元進(jìn)行具體的測(cè)試邏輯書寫用例,這樣就可以避免再添加新功能和頁(yè)面時(shí),頻繁對(duì)測(cè)試用例進(jìn)行修改了。
Puppeteer帶著上面我們的需求,我們來(lái)看下Puppeteer的功能和特性,是否能夠滿足我們的要求。
文檔地址
Puppeteer是一個(gè)Node庫(kù),它提供了一個(gè)高級(jí) API 來(lái)通過(guò) DevTools 協(xié)議控制 Chromium 或 Chrome。Puppeteer 默認(rèn)以 headless 模式運(yùn)行,但是可以通過(guò)修改配置文件運(yùn)行“有頭”模式。
我們可以使用Puppeteer完成以下工作:
訪問(wèn)頁(yè)面,進(jìn)行截圖
自動(dòng)進(jìn)行鍵盤輸入,提交表單
模擬點(diǎn)擊等用戶操作
等等等等。。
我們來(lái)通過(guò)一些小案例,來(lái)介紹他們的基本功能:
訪問(wèn)一個(gè)帶有ba認(rèn)證的網(wǎng)站puppeteer可以創(chuàng)建page實(shí)例,并使用goto方法進(jìn)行頁(yè)面訪問(wèn),page包含一系列方法,可以對(duì)頁(yè)面進(jìn)行各種操作。
(async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); // ba認(rèn)證 await page.authenticate({ username, password }); // 訪問(wèn)頁(yè)面 await page.goto("https://example.com"); // 進(jìn)行截圖 await page.screenshot({path: "example.png"}); await browser.close(); })();訪問(wèn)登陸頁(yè)面,并進(jìn)行登錄
首先,對(duì)于SPA(單頁(yè)面應(yīng)用),我們都知道,當(dāng)頁(yè)面進(jìn)入后,客戶端代碼才開(kāi)始進(jìn)行渲染工作。我們需要等到頁(yè)面內(nèi)容渲染完成后,再進(jìn)行對(duì)應(yīng)的操作。我們有以下幾種方法來(lái)使用
waitUntilpuppeteer針對(duì)頁(yè)面的訪問(wèn),切換等,提供了waitUntil參數(shù),來(lái)確定滿足什么條件才認(rèn)為頁(yè)面跳轉(zhuǎn)完成。包括以下事件:
load - 頁(yè)面的load事件觸發(fā)時(shí)
domcontentloaded - 頁(yè)面的DOMContentLoaded事件觸發(fā)時(shí)
networkidle0 - 不再有網(wǎng)絡(luò)連接時(shí)觸發(fā)(至少500毫秒后)
networkidle2 - 只有2個(gè)網(wǎng)絡(luò)連接時(shí)觸發(fā)(至少500毫秒后)
通過(guò)waitUnitl,我們可以當(dāng)頁(yè)面請(qǐng)求都完成之后,確定頁(yè)面已經(jīng)訪問(wèn)完成。
waitForwaitFor方法可以在指定動(dòng)作完成后才進(jìn)行resolve
// wait for selector await page.waitFor(".foo"); // wait for 1 second await page.waitFor(1000); // wait for predicate await page.waitFor(() => !!document.querySelector(".foo"));
我們可以利用waitForSelector方法,當(dāng)?shù)卿浛蜾秩境晒?,才進(jìn)行登錄操作
// 等待密碼輸入框渲染 await page.waitFor("#password"); // 輸入用戶名 await page.type("input#username", "username"); // 輸入密碼 await page.type("input#password", "testpass"); // 點(diǎn)擊登錄按鈕 await Promise.all([ page.waitForNavigation(), // 等跳轉(zhuǎn)完成后resolve page.click("button.login-button"), // 點(diǎn)擊該鏈接將間接導(dǎo)致導(dǎo)航(跳轉(zhuǎn)) ]); await page.waitFor(2000) // 獲取cookies const cookies = await page.cookies()針對(duì)列表內(nèi)容里的鏈接進(jìn)行批量訪問(wèn)
主要利用到page實(shí)例的選擇器功能
const table = await page.$(".table") const links = await table.$$eval("a.link-detail", links => links.map(link => link.href) ); // 循環(huán)訪問(wèn)links ...進(jìn)行錯(cuò)誤和訪問(wèn)監(jiān)聽(tīng)
puppeteer可以監(jiān)聽(tīng)在頁(yè)面訪問(wèn)過(guò)程中的報(bào)錯(cuò),請(qǐng)求等等,這樣我們就可以捕獲到頁(yè)面的訪問(wèn)錯(cuò)誤并進(jìn)行上報(bào)啦,這也是我們進(jìn)行測(cè)試需要的基本功能~
// 當(dāng)發(fā)生頁(yè)面js代碼沒(méi)有捕獲的異常時(shí)觸發(fā)。 page.on("pagerror", () => {}) // 當(dāng)頁(yè)面崩潰時(shí)觸發(fā)。 page.on("error", () => {}) // 當(dāng)頁(yè)面發(fā)送一個(gè)請(qǐng)求時(shí)觸發(fā) page.on("request") // 當(dāng)頁(yè)面的某個(gè)請(qǐng)求接收到對(duì)應(yīng)的 response 時(shí)觸發(fā)。 page.on("response")
通過(guò)以上的幾個(gè)小案例,我們發(fā)現(xiàn)Puppeteer的功能非常強(qiáng)大,完全能夠滿足我們以上的對(duì)頁(yè)面進(jìn)行自動(dòng)訪問(wèn)的需求。接下來(lái),我們針對(duì)我們的測(cè)試單元進(jìn)行個(gè)單元用例的書寫
最終功能通過(guò)我們上面對(duì)測(cè)試單元的規(guī)劃,我們可以規(guī)劃一下我們的測(cè)試路徑
訪問(wèn)網(wǎng)站 -> 登陸 -> 訪問(wèn)頁(yè)面1 -> 進(jìn)行基本單元測(cè)試 -> 獲取詳情頁(yè)跳轉(zhuǎn)鏈接 -> 依次訪問(wèn)詳情頁(yè) -> 進(jìn)行基本單元測(cè)試
-> 訪問(wèn)頁(yè)面2 ...
所以,我們可以拆分出幾個(gè)大類,和幾個(gè)測(cè)試單元,來(lái)進(jìn)行各項(xiàng)測(cè)試
// 包含基本的測(cè)試方法,log輸出等 class Base {} // 詳情頁(yè)單元,進(jìn)行一些基本的單元測(cè)試 class PageDetal extends Base {} // 頁(yè)面單元,進(jìn)行基本的單元測(cè)試,并獲取并依次訪問(wèn)詳情頁(yè) class Page extends PageDetal {} // 進(jìn)行登錄等操作,并依次訪問(wèn)頁(yè)面單元進(jìn)行測(cè)試 class Root extends Base {}
同時(shí),我們?nèi)绾卧诠δ茼?yè)面變化時(shí),跟蹤到測(cè)試的變化呢,我們可以針對(duì)我們測(cè)試的功能,為其添加自定義標(biāo)簽test-role,測(cè)試時(shí),根據(jù)自定義標(biāo)簽進(jìn)行測(cè)試邏輯的編寫。
例如針對(duì)時(shí)間切換單元,我們做一下簡(jiǎn)單的介紹:
// 1. 獲取測(cè)試單元的元素 const timeSwitch = await page.$("[test-role="time-switch"]"); // 若頁(yè)面沒(méi)有timeSwitch, 則不用進(jìn)行測(cè)試 if (!timeSwitch) return // 2. time switch的切換按鈕 const buttons = timeSwitch.$$(".time-switch-button") // 3. 對(duì)按鈕進(jìn)行循環(huán)點(diǎn)擊 for (let i = 0; i < buttons.length; i++) { const button = buttons[i] // 點(diǎn)擊按鈕 await button.click() // 重點(diǎn)! 等待對(duì)應(yīng)的內(nèi)容出現(xiàn)時(shí),才認(rèn)定頁(yè)面訪問(wèn)成功 try { await page.waitFor("[test-role="time-switch-content"]") } catch (error) { reportError (error) } // 截圖 await page.screenshot() }
上面只是進(jìn)行了一個(gè)簡(jiǎn)單的訪問(wèn)內(nèi)容測(cè)試,我們可以根據(jù)我們的用例單元書寫各自的測(cè)試邏輯,在我們?nèi)粘i_(kāi)發(fā)時(shí),只需要對(duì)需要測(cè)試的內(nèi)容,加上對(duì)應(yīng)的test-role即可。
總結(jié)根據(jù)以上的功能劃分,我們很好的將一整個(gè)應(yīng)用拆分成各個(gè)測(cè)試單元進(jìn)行單元測(cè)試。需要注意的是,我們目前僅僅是對(duì)頁(yè)面的可訪問(wèn)性進(jìn)行測(cè)試,僅僅驗(yàn)證當(dāng)用戶進(jìn)行各種操作,訪問(wèn)各個(gè)頁(yè)面單元時(shí)頁(yè)面是否會(huì)出錯(cuò)。并沒(méi)有對(duì)頁(yè)面的具體展示效果進(jìn)行測(cè)試,這樣會(huì)和頁(yè)面的功能內(nèi)容耦合起來(lái),就需要多帶帶的測(cè)試用例的編寫了。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/101902.html
摘要:抓取并生成預(yù)先呈現(xiàn)的內(nèi)容即。自動(dòng)表單提交,測(cè)試,鍵盤輸入等。創(chuàng)建一個(gè)最新的自動(dòng)化測(cè)試環(huán)境。使用最新的的和瀏覽器功能,直接在最新版本的瀏覽器中運(yùn)行測(cè)試。捕獲您網(wǎng)站的時(shí)間線跟蹤,以幫助診斷性能問(wèn)題。 木偶 Puppeteer 更友好的 Headless Chrome Node API木偶也是有心的 (=?ω?=) showImg(https://segmentfault.com/img/b...
摘要:前端每周清單第期現(xiàn)狀分析與優(yōu)化策略單元測(cè)試爬蟲作者王下邀月熊編輯徐川前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開(kāi)發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開(kāi)發(fā)教程工程實(shí)踐深度閱讀開(kāi)源項(xiàng)目巔峰人生等欄目。 showImg(https://segmentfault.com/img/remote/1460000011008022); 前端每周清單第 29 期:Web 現(xiàn)狀分析與優(yōu)化策略...
摘要:通過(guò)啟動(dòng)時(shí)的命令行參數(shù)僅能實(shí)現(xiàn)簡(jiǎn)易的啟動(dòng)時(shí)初始化操作。是谷歌官方出品的一個(gè)通過(guò)協(xié)議控制的庫(kù)。使用和例子類似其他框架,通過(guò)操作實(shí)例來(lái)操作瀏覽器作出相應(yīng)的反應(yīng)。簡(jiǎn)單例子的入門和實(shí)踐求贊,另外歡迎訪問(wèn)我的博客 出現(xiàn)的背景 Chrome59(linux、macos)、 Chrome60(windows)之后,Chrome自帶headless(無(wú)界面)模式很方便做自動(dòng)化測(cè)試或者爬蟲。但是如何和h...
摘要:視覺(jué)感知測(cè)試視覺(jué)回歸測(cè)試為了解決上面提到的各種問(wèn)題,視覺(jué)感知測(cè)試孕育而生。第三種方式,無(wú)法進(jìn)行視覺(jué)感知測(cè)試結(jié)果只能進(jìn)行視覺(jué)回歸測(cè)試和上一版的繼續(xù)比較差異。 前端自動(dòng)化測(cè)試 之 視覺(jué)測(cè)試 showImg(https://segmentfault.com/img/remote/1460000014720180); 前端測(cè)試分類 showImg(https://segmentfault.co...
閱讀 2415·2021-09-08 09:45
閱讀 3363·2021-09-08 09:45
閱讀 3106·2019-08-30 15:54
閱讀 3361·2019-08-26 13:54
閱讀 1417·2019-08-26 13:26
閱讀 1394·2019-08-26 13:23
閱讀 917·2019-08-23 17:57
閱讀 2187·2019-08-23 17:14