jest+enzyme爲react項目加入單測——1、安裝與配置

0. jest與其他測試框架的比較

由於筆者剛剛接觸測試,經驗還不是很豐富,所以只能說一些粗淺的認識。經過1周左右的調研,得出一個結論:

jest 約等於 karma + mocha + chai

也就是說,測試、斷言、覆蓋率等,jest一個人就都做了。如果一個東西能解決,而且沒有太大的性能問題的話,爲什麼不選擇它呢?而且他還是facebook的親兒子,用它來做react的單測,聽起來就很配~不過據說airbnb的enzyme寫react的單測,非常的方便,所以我們選擇建立一個jest + enzyme的測試框架。

先附上jest配置時需要解決的問題,方便大家查閱

1、.babelrc裏test環境關掉去掉modules: false

2、設置moduleNameMapper適配webpack.resolve.alias

3、設置transform轉換css等樣式文件

4、注意enzyme與react版本匹配問題

安裝jest也很簡單,通常我們都會用到babel,所以還會需要babel-jest

$ npm i -D jest babel-jest

後面還會安裝enzyme來測試react組件,由於需要匹配react版本,所以後面再細說。

1. jest的語法

import { sum } from '../../src/util';

describe('# sum test', () => {
  it('1 + 1 = 2', () => {
    expect(sum([1, 2])).toBe(3);
  });

  it('2 + 2 = 4', () => {
    expect(sum([2, 2])).toBe(4);
  });
});

不用多說什麼了,跟其他框架沒有什麼太大區別,import要測試的模塊,describe分塊,it分條件,expect運行。具體語法細節參考jest官方文檔。在測試普通js模塊時,基本不會碰到什麼問題,但是在測試react組件時,就會出現各種各樣的配置問題,尤其是與webpack搭配的時候。

2. jest的配置

jest的配置文件爲config目錄下的 jest.config.js(也可以起其他名字或者直接寫在package.json裏),用jest --config config/jest.config.js 來指定運行,先附上配置文件,然後再詳細說明配置時會遇到的問題。

const path = require('path');

module.exports = {
  rootDir: path.resolve(__dirname, '../'),
  collectCoverage: true, // 是否收集測試時的覆蓋率信息
  collectCoverageFrom: ['<rootDir>/src/**/*.{js,jsx,mjs}'], // 哪些文件需要收集覆蓋率信息
  coverageDirectory: '<rootDir>/test/coverage', // 輸出覆蓋信息文件的目錄
  coveragePathIgnorePatterns: ['/node_modules/', '<rootDir>/src/index.jsx'], // 統計覆蓋信息時需要忽略的文件
  moduleNameMapper: { // 主要用於與webpack的resolve.alias匹配,注意正則寫法
    '^src(.*)$': '<rootDir>/src$1',
    '^util(.*)$': '<rootDir>/src/util$1',
    '^assets(.*)$': '<rootDir>/src/assets$1',
    '^components(.*)$': '<rootDir>/src/components$1',
  },
  setupFiles: ['<rootDir>/test/setup.js'], // 運行測試前可運行的腳本,比如註冊enzyme的兼容
  testMatch: [ // 匹配的測試文件
    '<rootDir>/test/**/?(*.)(spec|test).{js,jsx,mjs}',
    '<rootDir>/src/**/__tests__/**/*.{js,jsx,mjs}',
  ],
  testURL: 'https://test.com?empty=&num=0&str=str&cstr=中文&encode=%e4%b8%ad%e6%96%87', // 運行環境下的url,默認about:blank
  transform: {
    '^.+\\.(js|jsx|mjs)$': '<rootDir>/node_modules/babel-jest',
    '^.+\\.(css|less)$': '<rootDir>/test/cssTransform.js',
    '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/test/fileTransform.js',
  },
  transformIgnorePatterns: [ // 轉換時需要忽略的文件
    '[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs)$',
  ],
};

另外還要在.babelrc裏面配置一下,請根據自己項目情況調整

{
  "presets": [
    ["env", {"modules": false }],
    "react",
    "stage-2"
  ],
  "env": {
    "test": {
      "presets": [["env"], "react", "stage-2"]
    }
  }
}

主要是在env.test裏面要把modules:false關掉,jest在運行單測時會默認走env.test裏面的配置。

1、webpack的resolve.alias引起的問題

如果運行時出現了類似如下的提示:

$ Cannot find module '@src/util' from 'index.jsx'

基本上就是在webpack的resolve.alias設置了@src的別名,而jest找不到。這個時候就需要配置moduleNameMapper了(見上文),配置的時候要注意正則的語法,還是有點難度的。

2、css等樣式引起的問題

如果運行時出現了類似如下的提示:

$ SyntaxError: Unexpected token .

基本上是因爲解析css等樣式問題出得問題,我們單測的時候其實不會去關心樣式的,所以我們需要把所有的樣式文件重定向一下,返回一個空的模塊,這樣就不會報錯了。

這個空文件可以寫成下面這樣,是從create-react-app裏借鑑過來的,然後配置transform屬性(見上文)即可,注意文件路徑,請根據自己的項目進行修改。新建test/cssTransform.js

module.exports = {
  process() {
    return 'module.exports = {};';
  },
  getCacheKey() {
    // The output is always the same.
    return 'cssTransform';
  },
};

除了樣式,我們還需要忽略圖片、字體等文件,就需要新建test/fileTransform.js文件。

const path = require('path');

// This is a custom Jest transformer turning file imports into filenames.
// http://facebook.github.io/jest/docs/en/webpack.html

module.exports = {
  process(src, filename) {
    return `module.exports = ${JSON.stringify(path.basename(filename))};`;
  },
};

3、window.location相關的問題

在測試中,我們的被測試代碼有可能會訪問window.location等url相關的參數,而node環境裏是沒有url的,這個時候就需要配置一下testURL屬性(見上文),來給jest(實際是jsdom)一個默認的url屬性,讓測試文件去訪問。這個配置暫時能夠解決一些簡單的問題,下面的文章會用到,但是目前還沒有發現能夠在單測文件裏動態修改url的辦法,請大牛多多指教。

3. enzyme的配置

需要根據項目的react版本來安裝對應的enzyme,詳見文檔,下面以[email protected]爲例,需要安裝。react@16就不需要安裝這麼多,所以要根據安裝時候的提示進行針對性的安裝。

$ npm i -D enzyme enzyme-adapter-react-15 [email protected] [email protected]

然後需要對enzyme進行一下配置

import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-15';

Enzyme.configure({ adapter: new Adapter() });

可以把這段代碼保存爲一個文件,如test/setup.js,然後在jest的配置文件裏設置setupFiles屬性(見上文),這樣就不需要在每個react組件的測試文件裏都重複寫這段代碼了。

用enzyme寫測試,就像寫jquery一樣,對於前端同學應該沒什麼難度。只要把組件包裹在shallow、mount或render裏面即可。至於這三者的區別,可詳見官方文檔,下面是個demo,還是比較容易看懂的。另外,我們會另起一篇來專門介紹如何寫組件的用例。

import React from 'react';
import { shallow, mount, render } from 'enzyme';
import AdjustRule from '../index';

describe('# Component AdjustRule', () => {
  it('should render without throwing an error', () => {
    expect(shallow(<AdjustRule />).contains(<p className="ajrl-title"></p>)).toBe(true);
  });

  it('should mount in a full DOM', () => {
    expect(mount(<AdjustRule />).find('.ajrl-title').length).toBe(1);
  });

  it('should render to static HTML', () => {
    expect(render(<AdjustRule />).find('.ajrl-title').text()).toEqual('test');
  });
});

4. 結束

實踐才能出真知,請看本系列的下一篇文章來上手實踐。不過你得首先有個react的開發環境,可參考《從零搭建前端開發環境》系列來搭建一個自己熟悉的react開發環境。本文的配置均基於如下目錄結構,請根據自身情況調整,注意領會精神。

demo
  |- config/
    |- jest.config.js
    |- webpack.base.js
    |- webpack.dev.js
    |- webpack.prod.js
  |- src/
    |- assets/
      |- logo.jpg
    |- components/
      |- HelloWorld/
        |- __tests__/
          |- index.spec.js
        |- index.jsx
        |- index.less
    |- util/
      |- index.js
    |- index.jsx
    |- index.less
  |- test/
    |- spec/
      |- util.spec.js
    |- .eslintrc.js
    |- cssTransform.js
    |- fileTransform.js
    |- setup.js
  |- .babelrc
  |- .eslintignore
  |- .eslintrc.js
  |- .gitignore
  |- .postcssrc.js
  |- index.html
  |- package.json

擴展閱讀:

React應用下的單元測試——by 阿里巴巴國際UED團隊

Testing React components with Jest and Enzyme

使用jest+enzyme進行react項目測試 - 測試手法篇

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章