轉載請註明:轉自http://blog.csdn.net/qiqingjin/article/details/53790729
原文鏈接:https://medium.com/writing-objects/how-to-unit-testing-react-components-3cc4e88327ae#.1ytffw11z
單元測試是定義我們構建的組件必須實現哪些功能的很好方式。它允許我們儘可能粒子化地測試我們組件的結構。我發現,單元測試使我在使用React時,寫更多功能性的代碼。我們將使用Karma,Jasmine和Enzyme開始React單元測試。
如果只對示例代碼感興趣,請戳 https://github.com/aktof/example-react-unit-testing.
什麼是Karma,Jasmine 和 Enzyme?
爲我們測試運行和寫斷言將用到這三個庫。它們定義了我們測試環境的不同方面。
Karma 是一個用來搜索測試文件、編譯它們然後運行斷言的測試器。
Jasmine 是一個斷言庫,它僅僅問“我們得到我們期待的東西了麼?”。它提供類似describe
,expect
和 it
的函數,也提供監聽一個函數或方法有沒有被觸發的監聽器。
Enzyme 是一個React測試工具庫。Enzyme提供渲染和遍歷React組件的方法,可以用來測試與React的render、mount和事件有關的斷言。
請記住,這不是進行單元測試的唯一方法。有許多不同的庫和配置可以完成同一件事。
示例應用程序
首先我們需要新建一個展示組件(dumb component) Button
。規則是它接受和渲染 label
prop,然後當它被單擊時調用 onClick
prop。
目錄結構:
lib/
src/
-- components/
----button/
------index.js
------spec.js
-- index.js
karma.conf.js
package.json
webpack.config.js
入口文件,即webpack開始打包的文件:
`// src/index.js`
import React from 'react';
import ReactDOM from 'react-dom';
`import Button from 'components/button';`
const App = () => (
<Button
label='Replicator'
onClick={() => console.log('Tea, Earl Grey, Hot.')} />
);
ReactDOM.render(
<App />,
document.getElementByID('root'),
);
入口文件中引入的Button
`// src/components/button/index.js`
`import React from 'react';`
`export default () => null;`
爲了構建我們的代碼,我們需要配置webpack入口文件、出口文件以及如何編譯代碼。下面是解析React的最基本配置。
最基本的webpack配置:
`// webpack.config.js`
`var path = require('path');`
module.exports = {
entry: [
path.resolve(__dirname, 'src'),
],
output: {
filename: 'app.bundle.js',
path: path.resolve(__dirname, 'lib'),
},
module: {
loaders: [
{
exclude: /node_modules/,
loader: 'babel',
test: /\.js$/,
},
{
loader: 'json',
test: /\.json$/,
},
],
},
resolve: {
root: path.resolve(__dirname, 'src'),
extensions: [
'',
'.js',
'.json',
],
moduleDirectories: [
'src',
'node_modules',
],
},
};
一旦完成以上配置,我們可以在package.json
中添加一個構件腳本來構件客戶端包。
`// package.json`
{
...
"scripts": {
...
"build": "webpack",
},
...
}
在終端中輸入npm run start
來運行它。
配置測試運行器
Karma在默認情況下會尋找一個叫karma.conf.js
的文件來定義測試環境。在這個文件中,我們可以配置測試文件、使用的瀏覽器、插件。
我們主要會用到 karma-jasmine
和 karma-webpack
兩個插件。
karma-jasmine
提供帶有Jasmine功能的環境。包括describe
,it
,expect
等。
karma-webpack
允許運行測試器編譯所有JavaScript文件 — 測試代碼和源代碼都包括。爲了達到這個目的,我們僅僅需要引入之前寫好的配置文件然後更新它。
`// karma.conf.js`
`var webpackConfig = require('./webpack.config.js');`
var config = function(config) {
return config.set({
basePath: '',
browsers: [
'PhantomJS',
],
frameworks: [
'jasmine',
],
files: [
'src/**/spec.js',
],
preprocessors: {
'src/**/*.js': [
'webpack',
'sourcemap',
],
'src/**/spec.js': [
'webpack',
'sourcemap',
],
},
/**
* 1) We need to define that these variables are available globally.
*/
webpack: Object.assign(
{},
webpackConfig,
{
externals: {
'react/addons': true,
'react/lib/ExecutionEnvironment': true,
'react/lib/ReactContext': true,
},
}
),
/**
* 2) Even if you're using express, the webpackServer option is required to compile code through karma-webpack.
*/
webpackServer: {
noInfo: true,
},
reporters: [
'progress',
],
colors: true,
autoWatch: true,
plugins: [
'karma-webpack',
'karma-jasmine',
'karma-sourcemap-loader',
'karma-phantomjs-launcher',
],
});
};
`module.exports = config;`
太棒了,現在我們有一個功能完備的運行測試器啦。我們可以喝杯酒看着機器運行,多麼享受的時光。
它是做什麼的?
在我們投入建立斷言環境前, 讓我們先弄明白單元測試是幹什麼的。簡而言之,目標是測試一個應用的最小片段。
當寫單元測試時,在頭部聲明“爲了讓這段代碼像我想的那樣運行至少需要什麼”。這會幫助你決定寫什麼樣的斷言。當你想維護你的基本代碼時,你可能會期待這些測試告訴你最近的更改或者模塊更新是否改變了組件的原功能。
寫一些斷言
現在到了最沉悶也最有趣的部分了,寫button的技術參數說明。我們想測試的三件事是:
組件
Button
可以渲染一個button
標籤。組件
Button
可以渲染傳給它的label文本。如果
onClick
prop存在,組件Button
可以調用它。
import React from 'react';
import {shallow} from 'enzyme';
`import Button from 'components/button';`
describe('Button', () => {
let wrapper;
const props = {
onClick: jasmine.createSpy('onClick'),
label: 'Fire Photon Torpedos',
};
beforeEach(() => {
wrapper = shallow(<Button {...props} />);
});
it('should contain a `button` element', () => {
expect(wrapper.is('button')).toBe(true);
});
it('should contain the label passed to it', () => {
expect(wrapper.text()).toBe(props.label);
});
it('should call the `onClick` handler when the button is clicked', () => {
wrapper.simulate('click');
expect(props.onClick).toHaveBeenCalled();
});
});
像“應該包含.someClassName
”這樣的DOM斷言怎麼辦?我個人更喜歡不檢查如此小的點,除非它是一種不同情況下渲染的產品。例如,在一個已經認證的組件中渲染<LoggedIn />
或者<LoggedOut />
取決於當前的認證狀態。
運行你的測試
我們將向package.json
中增加兩個script,這樣我們就可以很容易的運行單個測試或者一個時刻隨源碼改變的測試(類似webpack熱加載)。
`// package.json`
{
...
"scripts": {
...
"test": "karma start --single-run",
"test:watch": "karma start",
},
...
}
增加了這些後,運行npm run test:watch
!
☠️ Failure ☠️
確保一切正常
現在我們有一些有待實現的目標,現在讓我們完成它們。
`// src/components/button/index.js`
`import React from 'react';`
export default ({onClick, label}) => (
<button onClick={onClick}>
{label}
</button>
);
這是一個明顯的展示組件,它能完成我們的目標!
結論
希望這篇文章能讓你明白單元測試的重點是什麼,我們能用什麼工具寫和運行單元測試,如何自動化做這些。
完整的實例代碼請戳 https://github.com/aktof/example-react-unit-testing.