本文做者:Gil Tayar
編譯:鬍子大哈 javascript翻譯原文:blog.huziketang.com/blog/posts/…
英文鏈接:Testing Your Frontend Code: Part II (Unit Testing)html
轉載請註明出處,保留原文連接以及做者信息前端
上一篇文章《測試你的前端代碼 - part1(介紹)》中,我介紹了關於前端測試的基本知識,從本文開始將具體介紹測試技術。java
上一節有討論過,單元測試就是以代碼單元爲單位進行測試,代碼單元能夠是一個函數,一個模塊,或者一個類。不少人認爲大多數測試都應該叫單元測試,其實個人觀點仍是那句話,無所謂怎麼叫,名字叫什麼都行。只要你作了足夠多的測試,可以保證你部署到線上的生產代碼沒有問題就能夠了。node
單元測試是最容易理解、也最容易實現的測試方式。給單元測試一個輸入,讓它自動執行,將輸出結果和預期結果作對比看其是否正確(輸入能夠是一個函數參數,輸出就是函數的返回值)。react
在寫單元測試的時候,儘可能將你的單元測試獨立出來,不要幾個單元互相引用。養成這樣良好的測試習慣。webpack
第一節中提到過,爲了這系列博文,我寫了一個計算器應用,後面都會拿它進行測試。理論就講到這裏,一塊兒來看一下 Calculator 應用吧,源代碼在這裏。主要有兩個組件: keypad
和 display
,它們自身都是 React 單元,也都沒有引用其餘單元,後面會介紹如何對它們進行測試。git
(若是你已經看了代碼可能已經發現了我沒有使用 JSX。由於我不想進行轉譯。如今 Node 和全部流行的瀏覽器都已經徹底支持 ES6 了,那麼做爲一個例子來說,讓它直接運行會更好一些。雖然它不能運行在 IE 上,不過也不要緊,若是是一個真實的線上項目,我會進行轉譯的。)github
還有一個問題是按鍵和展現的邏輯問題,必需要有代碼來控制當點擊按鍵的時候發生什麼。這裏的按鍵包括數字鍵(如「1」,「5」)和操做鍵(如「+」,「=」)。按一般的作法,我把組件設計成了展現型組件(鍵盤)和容器型組件。容器型組件在個人 App 中是惟一包含 state 的組件,它要考慮當發生按鍵行爲的時候 App 內在邏輯的問題。web
處理邏輯問題的代碼是一個單獨的模塊——calculator。這個模塊對於單元測試是很完美的例子。由於它沒有對 I/O 和 UI 的依賴。你也應該儘可能使你的應用邏輯上保持獨立——模塊不依賴於 I/O 和 UI。
對於 Web 應用來說,I/O 是什麼?沒有文件和數據庫的操做?其實不只僅是這樣,還有 Ajax 調用,本地存儲,DOM 操做等,對我而言,任何和瀏覽器 API 有關的都是 I/O 操做。
我是怎麼把計算邏輯從 React 組件中分離出來的呢?其實很簡單,其內在邏輯是計算,我把他封裝到一個模塊中就能夠了。
這個模塊的實現也很容易——它接收一個計算器 state(一個對象)和一個字符(數字或者操做符),返回一個新的計算器 state。若是你用過 Redux,它很像 Redux 的 reducer 模式(若是你沒用過 Redux 也不要緊)。可是若是一直由上一個 state 獲取下一個 state,怎麼能回到初始狀態呢?這裏還有一個模塊叫作 initialState
,經過它能夠初始化計算器。計算器的 state 並非徹底黑盒的,它包含了一個字段叫作 display
,能夠把你想要展現的 state 顯示在計算器應用上。
若是你沒有耐心看源代碼的話,咱們一塊兒來看下這裏面最重要的部分,應用中算法的細節其實不重要。
module.exports.initialState = { display: '0', initial: true }
module.exports.nextState = (calculatorState, character) => {
if (isDigit(character)) {
return addDigit(calculatorState, character)
} else if (isOperator(character)) {
return addOperator(calculatorState, character)
} else if (isEqualSign(character)) {
return compute(calculatorState)
} else {
return calculatorState
}
}
//....複製代碼
再次強調這裏的實現細節並不重要,重要的是模塊的設計,它暴露出來的函數很是簡單——給一個 state,獲得下一個 state。
這就是咱們在 test-calculator
中所作的事情。那麼接下來怎麼進行測試呢?使用測試框架,目前比較流行的框架是 Mocha ,咱們就用它。不過像 Jest,Jasmine,Tape等框架也都行,隨意使用你喜歡的測試框架。
全部的測試框架都相似,寫測試代碼調用被測函數,經過測試框架運行他們,其中運行它們的代碼一般叫作「runner」。
Mocha runner 叫作 「mocha
」,若是你看測試腳本的 package.json
,能夠看到:
"scripts": {
...
"test": "mocha 'test/**/test-*.js' && eslint test lib",
...
},複製代碼
它會運行 test 文件夾中全部以 test-
開頭的文件,你能夠複製個人 repo,npm install
後,運行 npm test
本身試試。
(順便提一句,把全部測試都放在測試目錄,而且測試目錄放在 package 的根目錄是一個公認的 npm package 約定,若是你不想讓人以爲你不專業的話,最好仍是遵照這一約定。)
運行它,會獲得以下輸出:
這裏有 14 個測試經過的提示信息,若是沒經過,就會有紅色提示出現。
咱們看下面代碼:
const {describe, it} = require('mocha')
const {expect} = require('chai')
const calculator = require('../../lib/calculator')
describe('calculator', function () {
const stream = (characters, calculatorState = calculator.initialState) =>
!characters
? calculatorState
: stream(characters.slice(1),
calculator.nextState(calculatorState, characters[0]))
it('should show initial display correctly', () => {
expect(calculator.initialState.display).to.equal('0')
})
it('should replace 0 in initialState', () => {
expect(stream('4').display).to.equal('4')
})
//...複製代碼
首先引入 mocha
和斷言常量 expect
,這裏只引入咱們須要的函數:describe
,it
和 expect
。接下來引入咱們要測試的模塊 calculator
。
準備開始測試,用 it
函數來表達:
it('should show initial display correctly', () => {
expect(calculator.initialState.display).to.equal('0')
})複製代碼
it
函數接收一個字符串(用來表示測試結果)和一個函數(待測函數)。it
測試不能單獨運行,它們必須組成一個測試組。因此如代碼中所示,用 describe
函數定義測試組,裏面包含了若干個 it
函數。
測試函數中寫什麼呢?能夠寫任何想寫的東西,在這個例子中咱們測試了初始狀態所顯示的是否是 0
。若是咱們本身來實現怎麼實現呢,好比能夠像以下代碼:
if (calculator.initialState.display !== '0')
throw 'failed'複製代碼
對於這個問題,上面代碼也是能夠測出來的。可是 expect
包含了不少特性可使測試變得更簡單,好比能夠測試數組或者對象是否和一個給定的值相等。這就是單元測試的要點,即運行一個函數,或一組函數,檢查其 運行結果 是否和 預期結果 一致。
上面的很簡單對吧!其實對於單元測試來說,難的並非單元測試自己,而是分離代碼的藝術,把代碼儘可能分離成單元可測的模塊。單元可測的代碼通常都是不依賴於其餘模塊、不依賴於 I/O 的代碼。這是比較困難的,大多數人都傾向於把邏輯代碼、I/O 代碼和 UI 代碼寫到一塊兒。困難是困難,但不是說作不到,有不少技巧可使用,好比你的代碼中有一些驗證字段,那麼你就能夠把驗證代碼組織到一塊兒造成函數,再對這個驗證函數進行測試。
注意一個重要的事情——單元測試是在 NodeJS 下運行的!而計算器應用是運行在瀏覽器端的,上面的生產代碼都是在 NodeJS 下進行測試的,這也能夠嗎?
固然能夠。由於咱們的代碼是同構的,它能夠運行在瀏覽器端和 NodeJS 上。若是你的代碼沒有使用任何 I/O,就是說沒有對瀏覽器作任何的特化處理,那麼它就沒有理由不能運行在 NodeJS 上。另外,若是你使用了 require
,它既能夠被本地的 NodeJS 識別,也能夠被像 Webpack 同樣的打包器識別。你看代碼中的 package.json
,就能夠看到咱們就是使用了 Webpack,用 require
進行代碼打包:
"scripts": {
"build": "webpack && cp public/* dist",
...
}複製代碼
代碼中使用 require
來引入 React 或者其餘模塊,這不管是在 NodeJS 中仍是瀏覽器中都是通用的。
咱們還可使用另外一個測試框架,Karma 。使用它能夠在瀏覽器中運行 Mocha 代碼,可是這裏表達一下個人淺見:單元測試能在 Node 下運行就在 Node 下運行,由於很容易執行和 debug(固然如今在瀏覽器中執行也很方便)。而且若是代碼不須要轉譯的話,執行的也很是快。
可是咱們的代碼沒有在瀏覽器中測試確實是個問題,由於咱們並不真正地知道代碼在瀏覽器中運行會是什麼樣子。瀏覽器中的 JS 執行環境和 NodeJS 環境可能會有微妙的差異。
本文中主要介紹了什麼:
require
引入模塊、使用 Webpack 來打包模塊以使其符合瀏覽器運行環境。下篇文章咱們介紹端到端測試,把咱們的代碼在真實環境(瀏覽器)中測試。請看下一篇文章《測試你的前端代碼 - part3(端到端測試)》。
我最近正在寫一本《React.js 小書》,對 React.js 感興趣的童鞋,歡迎指點。