Puppeteer E2E測試入門

本文內容涉及ES6 asyncjest的相關知識,對於以上內容不太瞭解的讀者能夠先了解相關內容。javascript

Puppeteer是什麼

它由Chrome官方團隊提供,經過Devtools協議在Node層提供了一系列API來控制chrome或者chromium,也就是說咱們可以編寫Node環境的代碼便可對瀏覽器的行爲進行控制。經過它咱們能夠作到如下行爲:java

  • 生成頁面快照:圖片、pdfnode

  • 抓取spa應用生成預渲染頁面git

  • 自動化表單提交、UI測試、鍵盤輸入github

  • 抓取應用的性能數據(chrome performance timeline)chrome

  • 測試chrome擴展shell

固然以上行爲都是puppeteer能力中極小的一部分,更多的讀者能夠閱讀Puppeteer的文檔來了解,本文將介紹Puppeteer在E2E測試的實踐api

E2E測試

簡單來講,就是模擬真實用戶使用場景進行測試,預期應用可以正常響應用戶的操做,其關鍵點在於模擬用戶使用環境,模擬用戶操做。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

相關文章
相關標籤/搜索