Jest + Enzyme 前端自動化測試

Jest、Enzyme 簡介

Jest 是 Facebook 發佈的一個開源的、基於 Jasmine 框架的 JavaScript 單元測試工具。css

Enzyme 是 React 的測試類庫。 Enzyme 提供了一套簡潔強大的 API,並經過 jQuery 風格的方式進行DOM 處理,開發體驗十分友好。html

普通方法測試

首先,使用npm安裝Jestnode

npm install --save-dev jest

在目錄下新建一個待測試文件 sort.jsreact

function sort(sortArr) {
        return sortArr.sort((a, b) => a - b);
    }
    module.exports = sort;

此處sort方法未對入參作類型檢測jquery

在這裏定義了一個數組排序方法,下面來書寫其測試用例,在目錄下新建一個sort.test.js文件。webpack

const sort = require('./sort');
    const arr = [5,2,4,3,1];
    test('排序數組[5,2,4,3,1]', () => {
        expect(sort(arr)).toEqual([1,2,3,4,5]);
    })

在用例中,咱們先引入了待測試的方法,接下來定義了一個排序數組[5,2,4,3,1]的測試用例.test()用來定義一個測試用例,expect()會執行內部的方法,返回一個待測試的結果。toEqual()用來判斷返回的結果於指望的結果是否相等。這裏因爲指望返回結果爲數組,因此使用toEqual進行判斷,除此以外,還有toBe(),toBeNull()等方法來比較不一樣的類型。更多內容...git

打開package.json,在scripts中新增es6

test: "jest"

而後運行命令github

npm run test

會看到用例測試經過的信息web

avatar

因爲咱們的方法沒有作入參類型檢測,下面經過傳入字符串,來測試異常狀況。在sort.test.js中新增一個測試用例用例

test('排序字符串「52431」', () => {
        expect(sort('52431')).toEqual(12345);
    })

運行,則會看到測試失敗的信息

avatar

從測試結果中咱們能夠清除的看到,運行來兩個測試用例,第一個用例經過來,第二個用例運行是js出現了報錯。此時便能根據測試結果,調整代碼

更多測試方法此處不作討論,具體能夠參考Jest文檔

在具體項目中的使用

下面來在實際的項目中使用Jest + Enzyme來進行測試。測試Demo項目

首先,使用Create-React-App來建立一個應用。

接着,安裝jest

npm install --save-dev jest

因爲在書寫用例時,會用到es6語法,因此還要安裝babel-jest來進行轉碼

npm install --save-dev babel-jest

安裝enzyme

npm install --save-dev enzyme

也可使用react官方測試插件react-addons-test-utils,此處咱們使用enzyme,故不須要安裝。

此外,還須要根據使用的react版原本安裝enzyme-adapter-react。具體版本對照以下

enzyme-adapter-react版本 react版本
enzyme-adapter-react-16 ^16.4.0-0
enzyme-adapter-react-16.3 ~16.3.0-0
enzyme-adapter-react-16.2 ~16.2
enzyme-adapter-react-16.1 `~16.0.0-0 \ \ ~16.1`
enzyme-adapter-react-15 ^15.5.0
enzyme-adapter-react-15.4 15.0.0-0 - 15.4.x
enzyme-adapter-react-14 ^0.14.0
enzyme-adapter-react-13 ^0.13.0

此處demo使用的react版本爲^16.4.1,因此咱們須要安裝enzyme-adapter-react-16

npm install --save-dev enzyme-adapter-react-16

依賴安裝完成,接下來須要進行相關的配置。

首先配置package.json的測試命令test: "jest"

此時若是咱們在根目錄下建立一個.test.js文件,並書寫簡單的方法用例,執行測試命令,是能夠正常執行測試用例的。可是,咱們的項目卻並非簡單的單個方法但測試,實際項目中會存在這大量的組件依賴,還有cssimage等靜態資源的處理。因此,還要進行以下配置處理。

首先,咱們在package.json文件中新增一個jest的配置項

jest: {}

這裏咱們主要進行三個配置。

  • moduleFileExtensions表明支持加載的文件名。此處咱們的測試文件均以.js結尾,因此只配置成["js"]便可
  • transform用於編譯 ES6/ES7 語法,需配合 babel-jest 使用
  • moduleNameMapper表明須要被 Mock 的資源名稱。若是須要 Mock 靜態資源(如less、scss等),則須要配置 Mock 的路徑

jest默認會檢索項目內的*.test.js,*.test.jsx形式的文件並執行。當編寫當用例沒被jest檢索到時,可經過moduleDirectories來配置路徑。

在具體到組件測試時,爲了測試組件到交互性,咱們須要jest渲染出組件進行操做,此時,因爲咱們到項目中大量使用來webpack到依賴管理,以及less-loaderurl-loader等預編譯。在jest渲染組件是,沒法識別這些.less等文件。因此咱們須要經過mock來處理這些靜態文件。由於jest在渲染組件時,是不須要依賴css,image等靜態資源的。因此咱們能夠這樣配置:

"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
    "\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js"

前面經過正則來適配咱們須要匹配的靜態文件,後面爲咱們經過mock返回的數據。這裏咱們還須要在根目錄中建立__mock__的文件夾。在裏面新建fileMock.jsstyleMock.js兩個文件。

<!--fileMock.js-->
    module.exports = 'test-file-stub';
<!--styleMock.js-->
    module.exports = {};

這樣就能夠將測試集中在組件的結構和邏輯上。另外,可能在咱們的項目中,會使用大量的別名來簡化引用路徑,及webpack中的alias配置。此處一樣須要進行別名的配置,配置方式與靜態資源配置相似。一下是完整配置

"jest": {
        "moduleFileExtensions": [
            "js",
            "jsx"
        ],
        "moduleDirectories": [
            "src",
            "node_modules"
        ],
        "transform": {
            "^.+\\.js$": "babel-jest"
        },
        "moduleNameMapper": {
            "^components(.*)$": "<rootDir>/src/components$1",
            "^pages(.*)$": "<rootDir>/src/pages$1",
            "^utils(.*)$": "<rootDir>/src/utils$1",
            "^services(.*)$": "<rootDir>/src/services$1",
            "^static(.*)$": "<rootDir>/src/static$1",
            "^models(.*)$": "<rootDir>/src/models$1",
            "^variable(.*)$": "<rootDir>//src/static/less/variable.less",
            "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
            "\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js"
        }
  }

接下來建立一個待測試的組件,在src > pages文件夾中建立login組件,並配置好路由。組件代碼參考測試Demo項目
運行後頁面以下

<img src="https://github.com/ISummerRai...; width="320" />

接着,定義測試用例,此Demo定義來八個測試用例以下

  • 一、頁面title顯示「登陸」(UI)
  • 二、登陸帳號輸入手機號或郵箱時,帳號上方顯示登陸帳號
  • 三、登陸帳號輸入不爲手機號或郵箱,帳號上方顯示【帳戶輸入錯誤,請從新輸入】
  • 四、帳號輸入正常,密碼小於6位,登陸按鈕置灰。
  • 五、帳號輸入異常,密碼不小於6位,登陸按鈕置灰。
  • 六、帳號輸入正常,密碼不小於6位,登陸按鈕可點。
  • 七、點擊密碼後眼睛圖標,顯示密碼。
  • 八、顯示密碼狀態,再次點擊,隱藏密碼。

接下來,新建文件login.test.js來編寫測試用例代碼。
因爲用例中設計多個交互,因此咱們須要先渲染出組件。Enzyme爲咱們提供來三種渲染組件的方法shallowrendermount

  • shallow 方法就是官方的shallow rendering的封裝。
  • render 方法將React組件渲染成靜態的HTML字符串,而後分析這段HTML代碼的結構,返回一個對象。它跟shallow方法很是像,主要的不一樣是採用了第三方HTML解析庫Cheerio,它返回的是一個Cheerio實例對象。
  • mount方法用於將React組件加載爲真實DOM節點。

三種方法中,shallow render 返回的爲對象,用於分析HTML結構,因此沒法用於交互測試。mount方法加載的爲真實的DOM節點,因此可用於交互測試。本Login組件存在大量交互測試,因此使用mount建立組件,使用mount須要先使用Adapter配置以下

import Login from 'pages/Login';
    import React from 'react';
    import { configure } from 'enzyme';
    import Adapter from 'enzyme-adapter-react-16';
    import { mount } from 'enzyme';
    configure({ adapter: new Adapter() });
    
    const wrapper = mount(<Login />);

如今,咱們就可使用Enzyme的API來編寫測試用例了,Enzyme提供了豐富的類jquery風格的API,下面是部分API

.get(index):返回指定位置的子組件的DOM節點
    .at(index):返回指定位置的子組件
    .first():返回第一個子組件
    .last():返回最後一個子組件
    .type():返回當前組件的類型
    .text():返回當前組件的文本內容
    .html():返回當前組件的HTML代碼形式
    .props():返回根組件的全部屬性
    .prop(key):返回根組件的指定屬性
    .state([key]):返回根組件的狀態
    .setState(nextState):設置根組件的狀態
    .setProps(nextProps):設置根組件的屬性

完整API參見 Enzyme API

在前半部分的demo中,咱們使用來 test() 方法來編寫用例,此處,咱們使用

describe('', () => {
        it('', () => {})
    })

來編寫測試用例,這樣咱們能夠對測試用例進行分組

讓咱們來開始第一個用例「頁面title顯示「登陸」」的編寫

it('標題顯示', () => {
        const title = wrapper.find('.title').text();
        expect(title).toBe('登陸');
    })

這個用例十分簡單,僅僅在第一步獲取到了title中的文本,並對文本進行校驗。

第二個和第三個用例爲對輸入框輸入文本對校驗,此處,咱們能夠單獨對校驗方法進行測試,也能夠頁面對交互來完成測試。這裏用例經過交互來進行測試用例對編寫。因爲在輸入信息過程當中,校驗經過input框的onChange事件觸發,因此咱們須要用到 simulate 來觸發事件。其中一個用例以下

const accountInput = wrapper.find('.account').find('input');
    const accountTitle = wrapper.find('.account .name').find('span');
    it('輸入不合法帳號', () => {
        const event = {
            target: {
            value: 'abc123'
        }
    }
    accountInput.simulate('change', event);
    expect(accountTitle.text()).toBe('帳戶輸入錯誤,請從新輸入');
  })

模擬輸入來一個不合法的帳號‘abc123’,驗證失敗,顯示失敗信息。

在4,5,6三個用例中,須要獲取登陸按鈕Button組件的可點擊狀態,因爲enzyme沒法獲取 css 狀態,此時可使用API中的prop(key)來獲取組件的props狀態,從而判斷組件的可點擊狀態。其中一個用例以下

it('輸入正確帳號,密碼小於6位,指定狀態', () => {
    wrapper.setState({
      account: '18888888888',
      password: '12345',
      errorAccount: false
    });
    // 此處需從新獲取btn對象,不然會致使用例失敗
    const submitBtn = wrapper.find('.btn-box').find('Button');
    expect(submitBtn.props().disabled).toBe(true);
  })

此處經過直接設置state的值來更改Button的狀態。須要注意的是,爲來減小重複定義,許多Dom對象的獲取都在describe組下作了統一的定義,但在執行expect獲取按鈕狀態是,須要從新查找,來獲取最新但狀態。除了直接指定state狀態以外,還能夠經過輸入框輸入,change事件觸發但方式來完成用例,以下

it('輸入正確帳號,密碼小於6位,經過change觸發', () => {
    const accountEvent = {
      target: {
        value: '18888888888'
      }
    };
    const pwdEvent = {
      target: {
        value: '12345'
      }
    }
    accountInput.simulate('change', accountEvent);
    passwordInput.simulate('change', pwdEvent);
    const submitBtn = wrapper.find('.btn-box').find('Button');
    expect(submitBtn.prop('disabled')).toBe(true);

七、8兩個用例使用但方法與上面相同,再也不贅述。

全部用例編寫完成以後,執行npm run test能夠看到全部用例都經過測試。

測試覆蓋率

package.json 文件的 test 命令修改成

test: "jest --coverage"

執行 npm run test便可在用例執行信息後顯示用例的覆蓋率報告。

相關文章
相關標籤/搜索