本文內容涉及ES6 async
、jest
的相關知識,對於以上內容不太瞭解的讀者能夠先了解相關內容。javascript
它由Chrome官方團隊提供,經過Devtools協議在Node層提供了一系列API來控制chrome或者chromium,也就是說咱們可以編寫Node環境的代碼便可對瀏覽器的行爲進行控制。經過它咱們能夠作到如下行爲:java
生成頁面快照:圖片、pdfnode
抓取spa應用生成預渲染頁面git
自動化表單提交、UI測試、鍵盤輸入github
抓取應用的性能數據(chrome performance timeline)chrome
測試chrome擴展shell
固然以上行爲都是puppeteer能力中極小的一部分,更多的讀者能夠閱讀Puppeteer的文檔來了解,本文將介紹Puppeteer在E2E測試的實踐api
簡單來講,就是模擬真實用戶使用場景進行測試,預期應用可以正常響應用戶的操做,其關鍵點在於模擬用戶使用環境,模擬用戶操做。promise
那對於Web應用來講,用戶環境就是瀏覽器,用戶操做主要是移動、點擊,這些就是咱們須要模擬的部分,下面就直接進入環境和實踐部分。瀏覽器
本文的例子採用puppeteer
jest
jest-puppeteer
在mac環境實現
首先須要安裝以上依賴,這裏須要注意如下問題:
puppeteer會默認下載chromium,這裏能夠經過export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
跳過chromium下載
Node版本最好使用大於8的版本
接下來講明一下相關的配置
首先是jest相關的配置,在根目錄建立jest.config.js
//jest.config.js
const config = require('config');
const _ = require('lodash');
module.exports = {
preset: 'jest-puppeteer', //調用preset
globals: _.assign({}, config.get('e2e.variable'), { //這裏能夠注入全局變量
ENV_URL: config.get('baseUrl')
}),
testMatch: ['**/__e2e__/**/*.test.js?(x)'] //指定須要進行測試的文件
};
複製代碼
接下來就是配置puppeteer,在根目錄建立jest-puppeteer.config.js
//jest-puppeteer.config.js
module.exports = {
launch: {
headless: true, //設定運行模式,false的狀況下將會工做在有GUI界面的模式,true則不開啓GUI界面
executablePath: //設定本地Chrome路徑,官方推薦使用Chrome Canary
'/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary'
}
};
複製代碼
環境已經配置好了,是時候進入實踐環節了。對於絕大部分系統尤爲是管理系統來講,第一步要作的確定是登陸系統,所以,咱們的第一個實踐就是用puppeteer腳本模擬登陸,這裏的例子基於做者這邊的一個系統的登陸流程。
const loginFunc = page => async () => {
await page.setViewport({ width: 1280, height: 720 }); //設置窗口大小
await page.goto(`${ENV_URL}/login`); //前往登陸地址
const loginIframe = page.frames().find(f => f.name() === 'login-iframe'); //找到登陸iframe
await loginIframe.waitForSelector(
'#login-tab-container > div.tab-item.tab-right'
);
await loginIframe.click('#login-tab-container > div.tab-item.tab-right'); //切換登陸TAB
await page.waitFor(1000); //特殊用途,以後說明
const username = await loginIframe.waitForSelector('#username'); //找到輸入框
await username.type(USERNAME); //輸入用戶名
const pass = await loginIframe.waitForSelector('#password');
await pass.type(PASSWORD); //輸入密碼
await loginIframe.click('#login-btn'); //點擊登陸按鈕
await page.waitForNavigation(); //等待跳轉導航完成
};
module.exports = {
loginFunc: loginFunc
};
複製代碼
在這個例子中,展現了打開登陸頁面,輸入用戶名、密碼並進行登陸的流程,主要涉及到了setViewport
goto
frames
waitForSelector
click
waitFor
type
waitForNavigation
這些API,下面說說其中幾個比較重要的API,更多的能夠參考官方文檔;
setViewport
設置窗口大小,返回一個promise
waitForSelector
參數爲CSS Selector,返回一個promise,直到指定的元素出現纔會resolve,超時後會reject
click
點擊某個元素,返回一個promise
type
向某個元素輸入內容,返回一個promise
waitForNavigation
URL改變時觸發,返回promise,導航結束時resolve,經過history api進行URL更改時也能夠經過該方法等待導航結束
frames
能夠獲取頁面中全部的iframe
複製代碼
相信讀者看完這段例子後也能感覺到puppeteer的API設計是十分語意化的,很是好懂。可是實際上這個例子是存在幾個踩坑點的,這裏說一下筆者從這個例子中收穫的經驗:
waitForSelector只有當目標Selector本來不存在DOM中才會生效,若是目標元素是經過display: none;
visibility: hidden
這類方式進行切換的話,這個方法是直接resolve的。
若是這個切換過程當中還存在動畫效果,那在這個動畫效果的過程當中,不管是click仍是type動畫過程當中的元素,操做都是不會生效的,必須等到動畫結束,所以在本例子中還增長了waitFor(1000)
這句代碼來等待動畫的結束
至此咱們已經完成了登陸,接下來就能夠和jest結合進行自動化測試了,先放出本部分的例子:
const utils = require('./utils');
jest.setTimeout(10000);
describe('e2e test', () => {
beforeAll(utils.loginFunc(page));
it('e2e test-1', async () => {
const el = await page.waitForSelector(
'#root > .services_C2FC97 > .ant-row > .ant-col-8:nth-child(1) > .service-card_C2FC97'
);
expect(await el.$eval('p', node => node.innerText)).toBe('test'); //獲取指定元素的innerText
});
});
複製代碼
在這個例子裏,咱們先引入了上面編寫的loginFunc來做爲每個測試的前置條件,因爲使用了jest-puppeteer,運行環境中會自動注入puppeteer的page和browser對象,所以能夠直接調用;這個例子中測試的是首頁的一張功能卡片的標題是否爲test
;這個例子是很基礎的,不過相信經過上面那個登陸的例子,讀者已經瞭解到編寫E2E測試腳本的套路了。
不過這裏還有一點須要說明,因爲puppeteer啓動時間和打開網頁的耗時比較難以估計,須要經過jest.setTimeout(ms)
來延長一個測試執行的時間,不然有可能由於執行超時致使失敗,下面是這個例子最終的執行結果:
yarn e2e
yarn run v1.9.4
$ cross-env NODE_ENV=test jest -c jest.config.js
Determining test suites to run...
DevTools listening on ws://127.0.0.1:54751/devtools/browser/7d8d289a-5335-4ffc-a65c-f7e98cb34de0
[1129/154120.584773:WARNING:spdy_session.cc(3152)] Received HEADERS for invalid stream 25
PASS __e2e__/demo.test.js (6.931s)
e2e test
✓ e2e test-1 (1540ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 7.046s
Ran all test suites.
✨ Done in 10.72s.
複製代碼
至此,入門教程就結束了,是否感受到很像按鍵精靈?但實際上經過puppeteer咱們能夠實現更強大的一些控制,包括監聽網絡請求,監聽頁面建立等等,基本上咱們能在瀏覽器手動作到的,經過它提供的Node API也可以作到,這部份內容歡迎讀者去閱讀文檔進一步的進行探索;
那E2E測試究竟能爲咱們帶來什麼呢?在筆者看來,對於一個長期迭代的項目,隨着項目規模的擴大,功能的迴歸測試耗費的時間會不可避免的增長,在這種狀況下,若是有自動化測試可以幫咱們進行部分測試,將大大提升咱們的效率;同時經過定時測試任務能夠更早的發現產品功能上存在的問題,從而保證產品可以按時交付,這是它帶來的最大的價值。
最後,推薦給你們一個小工具puppeteer-recorder
,這是一個Chrome插件,能夠方便的錄製測試腳本,從而擺脫手動編寫腳本的苦惱😄(因爲同源策略,該工具不支持iframe內的操做錄製)
@author: Monado