前端UI自動化測試(TypeScript+Jest+Puppeteer)

最近幾個月來,筆者一直在探索前端UI自動化測試的場景和方案。最初的時候,面對衆多的技術選型,顯得有些茫然,而團隊此前也沒有太多關於這方面的經驗,只能一步一步摸索總結,固然期間也踩過很多坑,最終造成了一套相對穩定的測試方案,將來還將繼續擴展和完善。前端

項目地址: jest-puppeteer-testing

在這篇文章中,筆者想和你們分享一下本身對於UI自動化測試的思考和經驗。git

爲何要進行UI自動化測試

業務的更新迭代頻繁,傳統測試大部分都仍是手工、肉眼的模式來進行,沒法知足產品敏捷開發、快速迭代的需求。而UI自動化能讓全功能的迴歸變得簡單,釋放純手工測試的人力資源,而且迴歸測試可以覆蓋到全部的邏輯場景,這對測試的效率,以及整個開發流程的效率都是很大的提高,而且可以規避不少人的主觀和客觀因素致使的漏測或者疏忽。github

其餘測試方式的侷限性:api

單元測試(Unit Testing)服務器

事實上,單元測試確實可以幫助咱們發現大部分的問題,可是在複雜的前端交互中,單純的單元測試並不能真實地反映用戶操做的路徑,而單元測試通常的場景是測試一系列的功能集合。網絡

快照測試(Snapshot Testing)框架

DOM結構並不能徹底反映頁面的視覺效果,DOM結構不變並不徹底等於樣式不變。此外,大多數工具都是React專用,非React應用基本不支持。async

筆者想說:工具

不少人認爲,UI老是頻繁的變更,致使測試用例維護成本高,性價比低,所以UI自動化測試比較適合場景穩定的業務。其實不是,這裏的UI不只僅指的是視覺,更多的是業務邏輯。UI能夠多變,但業務邏輯必定是趨於穩定的,尤爲是核心業務,想想用戶得多辛苦才能適應這種業務邏輯頻繁變動的產品啊。

關於技術選型

TypeScript + Jest + Puppeteer

事實上,對於UI自動化測試來講,許多框架之間並無太多差異,也歷來不是影響整套測試用例是否健壯的關鍵性因素。相比之下,如何提升測試用例穩定性及全面性纔是讓UI自動化測試方案落地的重要細節。單元測試

開發實踐

項目搭建

你們能夠參考jest-puppeteer-testing,這裏再也不累述。

核心文件

// setup/expect-image-snapshot.ts
// 讓jest支持保存/比對屏幕截圖
import { configureToMatchImageSnapshot } from 'jest-image-snapshot';

expect.extend({
  toMatchImageSnapshot: configureToMatchImageSnapshot({
    customSnapshotsDir: '__image_snapshots__',
  }),
});
// setup/enhance-puppeteer.ts
// 加強puppeteer功能,如:攔截請求並使用mock數據
import { onRequestInterceptor } from '../utils/request';

jest.setTimeout(30000);

beforeAll(async () => {
  page.on('request', onRequestInterceptor); // 攔截請求,使用代理數據
  await page.setRequestInterception(true);
});
// utils/request.ts
// mock數據的核心文件
// 這裏只攔截xhr或fetch請求,固然你也能夠自行擴展
import { URL } from 'url';
import { Request } from 'puppeteer';
import mocks from '../mocks';

// 設置請求攔截器的數據,用於同一請求返回不一樣結果,生效一次後自動銷燬
export const interceptors: { [api: string]: any } = {};
export const setRequestInterceptor = (api: string, value: any) => {
  interceptors[api] = value;
};

export const onRequestInterceptor = (request: Request) => {
  const resourceType = request.resourceType();
  if (resourceType === 'xhr' || resourceType === 'fetch') {
    const location = new URL(request.url());
    const mockKey = location.pathname;
    if (mockKey && mocks.hasOwnProperty(mockKey)) {
      const mock = mocks[mockKey];
      let response: any;
      if (typeof mock === 'function') {
        response = mock({ location, request, interceptor: interceptors[mockKey] });
        delete interceptors[mockKey]; // 生效一次後自動銷燬
      } else {
        response = mock;
      }
      if (response) {
        if (response.body != null && typeof response.body === 'object') {
          response.body = JSON.stringify(response.body);
        }
        request.respond(response);
      }
    } else {
      request.continue();
    }
  } else {
    request.continue();
  }
};

其餘文件

  • shared.d.ts定義數據類型
  • cases目錄下存放測試用例
  • mocks目錄下存放mock數據
  • utils目錄下存放工具方法

補充說明:關於mock的類型定義,能夠在shared.d.ts中找到,固然你也能夠在這裏增長其餘類型定義

經驗總結

測試地址的選擇(本地/線上)

  • 本地服務器:請求響應快,測試結果穩定,但沒法排除由線上環境差別或代碼打包過程當中引起的問題
  • 線上服務器:可以反映網站真實的展現,無需額外啓動服務器,任什麼時候候均可以測試,但受網絡因素影響,可能致使測試結果不穩定

儘可能抹平不肯定因素帶來的影響

如維持數據請求的結果穩定,日期時間穩定,保證頁面渲染的一致性。假如因爲數據返回或時間的不肯定性,致使每次頁面渲染不同,那這樣測試也失去了意義。

儘可能明確保存屏幕截圖的時機

如訪問一個頁面後截圖,因爲網絡因素的緣由,圖片資源並非每次都加載完成,從而致使截圖先後不同。

......(暫時寫這麼多,有空再更)

總結

事實上,這套UI自動化測試方案更像是端到端測試(E2E Testing),即模擬一個用戶將程序做爲一個徹底的黑盒,打開應用程序模擬輸入,檢查功能以及界面是否正確,配合屏幕截圖能夠直觀感覺到用戶進行某些交互產生的具像化視覺效果。

項目地址: jest-puppeteer-testing
相關文章
相關標籤/搜索