UI自動化測試 vs. 單元測試
在前面很多文章中,我們都介紹了單元測試。如果你了解單元測試,或者讀過我之前寫的單元測試的文章,那么你一定知道,單元測試的測試對象是單獨的、隔離的小代碼片段或者代碼單元。與單元測試不同,端對端測試的測試對象則是頁面上的用戶交互,我們對底層實現一無所知,也就是說我們的測試是黑盒的。另外,一些跨頁測試,比如鏈接檢查,登陸跳轉等功能必須使用端對端測試才能檢查出來,單元測試是無法測這些功能的。以前我只寫單元測試,不寫端對端測試,結果有一次所負責的頁面上有個鏈接不能點了,還好及時修復,但還是讓我感受到了端對端測試,或者說是自動化端對端測試的重要性。
Puppeteer 默認情況下,所有操作是不可見的,如果你想像我這樣監視發生的一切,需要將 Puppeteer 的
headless
選項設為false。
使用 Puppeteer 進行瀏覽器自動化
我使用過很多端對端測試的輪子,比如 Selenium、Appium、Protractor、Zombie.js、Cypress、Nightmare、Puppeteer 等。但最終還是選擇了 Puppeteer,因為 Selenium 和 Appium 太難用了,Protractor 則像是專門給 Angular 設計的,Zombie.js 太簡單了,而且使用的瀏覽器內核不是市面上流行的任何一個,而是自定義的。Cypress 有平台依賴,我只是想要個本地運行的工具而已。只剩 Nightmare 和 Puppeteer 了,其實這兩個都是好選擇,但是我是個 star 控,Puppeteer 的 star 比 Nightmare 多,所以我選擇了 Puppeteer。但事實上 Nightmare 更流行,因為我發現螞蟻最新的那個 Antd Pro 就是用的 Nightmare,阿里一些其他端對端測試的工具也有基於 Nightmare 來做的。所以如果你想使用 Nightmare 來進行自動化端對端測試也是完全沒有問題的。
使用 Puppeteer 非常簡單,首先安裝它:
yarn add puppeteer # or "npm i puppeteer"
const puppeteer = require('puppeteer');然后就可以在 Node 腳本中使用它了!來個簡單的導航並截屏例子吧!這個例子先啟動瀏覽器,導航到 https://baidu.com
頁面,然后截屏並保存為 baidu.png
,最后關閉瀏覽器。
(async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('https://baidu.com'); await page.screenshot({path: 'baidu.png'}); await browser.close(); })();
將上述代碼寫進 Node 腳本中,並運行就可以了!看下生成的截圖:
是不是很簡單?短短幾行代碼就做了這么多事。如果你對 async
、await
這種語法不熟悉,那么我強烈建議你去學習一下,這種語法在 Puppeteer 中使用率簡直不要太高。不過也不要擔心學習成本, async
、await
語法非常簡單,就是 Promise 的一種新寫法而已,讓你的異步代碼看起來就像是同步的一樣。
使用 Jest 來進行測試
要知道,Puppeteer 是一個瀏覽器自動化工具,它只能進行瀏覽器的自動化,本身並不具有測試功能。我說的測試功能指的是,斷言啊,生成測試報告啊這些功能。如果你不熟悉這些概念,那么請移步:《Jest 單元測試入門》。所以,除了 Puppeteer 外,我們還需要使用一個測試工具,我選擇了 Jest,理由在之前的博文中已經說過很多遍了,這里不再贅述。使用 Jest 非常簡單,只需要
- 安裝 Jest
- 編寫測試腳本 *.test.js
- 最后在終端中輸入 jest 命令運行測試
具體用法看之前的博文:《Jest 單元測試入門》。
將 Jest 與 Puppeteer 結合使用
講完了 Puppeteer 和 Jest 的基本用法,我們來看一下,如何將兩者結合起來使用。其實將 Jest 與 Puppeteer 結合使用非常簡單,因為 Puppeteer 的本質就是個 NPM 模塊而已,所以我們只需要在 Jest 測試腳本中引入它即可使用了。為何如此呢?因為測試腳本的本質其實也是 Node 腳本,既然是 Node 腳本那么當然可以直接引入 NPM 模塊來用了!
需要注意的是,因為 Puppeteer 通常需要使用
async
、await
這種語法,如果你的 Node 版本在7.6及以上,那么恭喜你,直接大膽使用,否則需要在 Jest 中配置 Babel,來使其支持這種新語法。在 Jest 中配置 Babel 非常簡單,你可以在這里找到具體方法。
讓我們來個小例子吧!首先,我們打開百度頁面,並斷言百度頁面的 title
是 百度一下,你就知道
。那么測試腳本應該這么寫:
const puppeteer = require('puppeteer'); test('baidu title is correct', async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('https://baidu.com'); const title = await page.title(); expect(title).toBe('百度一下,你就知道'); await browser.close(); });
$ npm test看到 test
和 expect
兩個全局函數了嗎?這就是 Jest 所賦予的能力,讓你可以編寫測試用例和斷言。最后在命令行輸入 npm test
,即 jest
(這是在 package.json 中配置好的命令),即可看到生成的測試報告:
> fe-test@1.0.0 test /Users/liuyiqi/code/fe-test > jest PASS puppeteer-demo/baidu-title.test.js PASS puppeteer-demo/screenshot.test.js Test Suites: 2 passed, 2 total Tests: 2 passed, 2 total Snapshots: 0 total Time: 3.241s Ran all test suites.
https://github.com/lewis617/fe-test/tree/master/puppeteer-demo其中 screenshot.test.js
是截屏的那個例子,baidu-title.test.js
是斷言百度首頁 title 的例子。你可以在這里找到源碼:
至此,使用 Jest 與 Puppeteer 來進行端對端測試的基本用法就講完了。下篇博文我們將會集中講解常用 Puppeteer 功能,比如模擬用戶輸入、執行 JavaScript 腳本、獲取某個 DOM 節點中的文本等。