使用Jest對原生TypeScript專案進行UI測試

二哲發表於2018-02-19

前戲

使用Jest對原生TypeScript專案進行UI測試

最近寫了一個 wechat-colorpicker 小專案。 主要是為了練習下TS。既然寫了一個小庫,我就想著順便學下如何寫測試吧,這是一件蠻有意思的事情。

從選型到搭建環境,前前後後用了近2個小時。不得不說一個合格的前端必然是一個合格的配置工程師。再次列舉下,這個專案中所需要搭建配置的工具。

  • webpack.config 自動編譯ts+css
  • tsconfig.config ts的配置檔案
  • tslint.json tslint的配置檔案
  • jest.config 配置jest
  • .babelrc jest解析js時還會需要用到的外掛
  • circle.yml CircleCI 配置檔案

如果大家有什麼不懂的,自行百度

Jest + TS 入門

第一個問題,我專案都是TS寫的,自然會有 import 這樣的語法怎麼辦?

通過官網的Getting started 我們可以在最下方找到 ts-jest 不難理解,我們需要配的其實就是jest載入到什麼樣型別的檔案,使用什麼預處理來處理檔案。

transform: {
    '.*\\.(ts)$': '<rootDir>/node_modules/ts-jest/preprocessor.js',
},
複製程式碼

transform 就是專門用來匹配各種檔案字尾,然後進行對應的預處理,你可以理解為webpack裡的loader

我在TS中引入了.css檔案咋辦?同上

既然有transform,那我們任何檔案都可以通過transform進行預處理了。

transform: {
    '^.+\\.js$': '<rootDir>/node_modules/babel-jest',
    '.*\\.(ts)$': '<rootDir>/node_modules/ts-jest/preprocessor.js',
    '^.+\\.(css)$':"<rootDir>/node_modules/jest-css-modules"
},
複製程式碼

如果是js檔案我通過babel-jest處理,css則使用jest-css-modules。假如沒有這些配置,那import了你的庫,庫裡有引入了高特性的js檔案,或者css檔案就會編譯報錯。

關於rootDir

在進行技術選型的過程中,我看了最新版本的vue-cli裡推薦用哪些框架進行測試,一個是jest,還一個是krama+mocha。 我選擇了jest,jest本身是fb出的,對於react非常友好。本身也做了許多環境上的封裝切換jsdom環境或者node環境非常方便。我最後選擇了這個。

剛剛開始看vue-cli裡的jest配置我是拒絕的,第一個最顯眼的關鍵字就是<rootDir>這種像XML得東西。但是你慢慢靜下心來去理解就很容易了,其實就是一個basePath的感覺。我們可以看下文件怎麼說 rootDir

我的目錄如下

--- __test__
    jest.config.js
--- dist
--- node_modules
--- src

複製程式碼
//jest.config.js
module.exports = {
      rootDir: path.resolve(__dirname, '../'),
}
複製程式碼

<rootDir>其實就代表根目錄了

setupFiles 選項

setupFiles是一個AOP的配置,我覺得這個非常好用!因為jest是通過jsdom是模擬了一個document的執行環境,那必然還有很多可能是沒有的,比如localStorage,那我們可以通過該配置來設定我們啟動前,需要載入什麼,比如vue-cli中就是設定了Vue.config.productionTip = false,而我們可以讓環境支援localStorage。

setupFiles: ["jest-localstorage-mock"]
複製程式碼

不難發現,其實jest的生態還是很豐富的,我本次遇到的問題谷歌幾個關鍵字很快都能解決

UI Test 該怎麼寫?

test應該是像純函式一樣保證輸入輸出都是一樣的,UI test一方面與Dom耦合,另一方面又使用者互動耦合,那具體應該怎麼寫呢?

思路是:模擬使用者操作,再通過Dom進行判斷是否渲染正確。

比如這個例項化的測試,我們可以測試是否初始化是否正常,通過jquery來輔助判斷

// 例項化測試
import WeChatColorPicker from '../src';
import $ from 'jquery';

export function newInstance() {
  document.body.innerHTML = `<div id="container"></div>`;
  return new WeChatColorPicker(pickerOptions);
}

const baseColorArr = [ ... ];

test('test new instance', () => {
  const instance = newInstance();
  expect(instance.baseComponent.baseColorArr).toEqual(baseColorArr);
  expect($('.wechat-colorpicker')).not.toBeNull();
});

複製程式碼

比如這個是點選【基本色】【更多顏色】我們會切換class,那就可以像這樣

// tab class切換的互動測試
import $ from 'jquery';
import { newInstance } from './utils';

describe('change tab test', () => {
  newInstance();
  test('use base color', () => {
    $('.wechat-picker-box p i').eq(0).click();
    expect($('.wechat-colorpicker').hasClass('base-color')).toBe(true);
  });

  test('use picker', () => {
    $('.wechat-picker-box p i').eq(1).click();
    expect($('.wechat-colorpicker').hasClass('more-color')).toBe(true);
  });
});
複製程式碼

是不是突然就覺得非常簡單了?並且是唯一性的,測試用例可靠性也有保障。 之後我們就只需要配合一個CI,每次提交前跑一邊我們的測試程式碼,所有用例測試成功即可pr,否則直接被拒絕。

寫完了測試,給我們的jest.config 多加一行配置,來生成我們的測試報告(Jest內建了istanbul)

module.exports = {
  // ...
  collectCoverage: true,
  // ...
}
複製程式碼

接著執行下 npm t 檢視測試結果如下

wechat-colorpicker-06

  • % Stmts 是語句覆蓋率(statement coverage):是否每個語句都執行了?
  • % Branch 分支覆蓋率(branch coverage):是否每個if程式碼塊都執行了?
  • % Funcs 函式覆蓋率(function coverage):是否每個函式都呼叫了?
  • % Lines 行覆蓋率(line coverage):是否每一行都執行了?

更多測試用例前往 >>> repo-wechat-colorpicker <<< 檢視

CricleCI(番外篇)

我們可以通過CI的工具來完善我們的wordflow,在這我選用了CricleCi。進入官網我們直接github登入後,setup 我們的專案。

wechat-colorpicker-02

然後根據它的推薦走,在我們專案根目錄新增一個cricle.yml,複製黏貼它的推薦配置即可。

然後我們push測試一下,在這裡我寫錯了我的檔案路徑,所以構建報錯了。

wechat-colorpicker-03

重新修復了問題後,就可以正常執行工作了。

wechat-colorpicker-04

由於本文不是重點介紹CI,這裡就不過多展開了,有興趣的朋友可以自己摸索下

後面真的沒有了

至此,你應該對前端UI測試應該大致有一個巨集觀的瞭解。

本文沒有過多得介紹Jest的用法或者語法,希望可以給不知道如何做測試的朋友們一點方向,自己去嘗試找到適合自己專案的才是最好的。

剛剛開始可能很難,無從下手,成本很大。實際上做起來,其實都是慢慢的套路,寫熟練了後應該會上癮,畢竟最後跑完測試的那感覺會讓你達到高潮。

相關文章