摘要:單元測試上一節(jié)有討論過,單元測試就是以代碼單元為單位進(jìn)行測試,代碼單元可以是一個函數(shù),一個模塊,或者一個類。單元測試是最容易理解也最容易實現(xiàn)的測試方式。在寫單元測試的時候,盡量將你的單元測試獨立出來,不要幾個單元互相引用。
本文作者:Gil Tayar
編譯:胡子大哈翻譯原文:http://huziketang.com/blog/posts/detail?postId=58d3de1e7413fc2e8240855b
英文連接:Testing Your Frontend Code: Part II (Unit Testing)
轉(zhuǎn)載請注明出處,保留原文鏈接以及作者信息
上一篇文章《測試你的前端代碼 - part1(介紹)》中,我介紹了關(guān)于前端測試的基本知識,從本文開始將具體介紹測試技術(shù)。
單元測試上一節(jié)有討論過,單元測試就是以代碼單元為單位進(jìn)行測試,代碼單元可以是一個函數(shù),一個模塊,或者一個類。很多人認(rèn)為大多數(shù)測試都應(yīng)該叫單元測試,其實我的觀點還是那句話,無所謂怎么叫,名字叫什么都行。只要你做了足夠多的測試,能夠保證你部署到線上的生產(chǎn)代碼沒有問題就可以了。
單元測試是最容易理解、也最容易實現(xiàn)的測試方式。給單元測試一個輸入,讓它自動執(zhí)行,將輸出結(jié)果和預(yù)期結(jié)果做對比看其是否正確(輸入可以是一個函數(shù)參數(shù),輸出就是函數(shù)的返回值)。
在寫單元測試的時候,盡量將你的單元測試獨立出來,不要幾個單元互相引用。養(yǎng)成這樣良好的測試習(xí)慣。
測試 Calculator 應(yīng)用第一節(jié)中提到過,為了這系列博文,我寫了一個計算器應(yīng)用,后面都會拿它進(jìn)行測試。理論就講到這里,一起來看一下 Calculator 應(yīng)用吧,源代碼在這里。主要有兩個組件: keypad 和 display ,它們自身都是 React 單元,也都沒有引用其他單元,后面會介紹如何對它們進(jìn)行測試。
(如果你已經(jīng)看了代碼可能已經(jīng)發(fā)現(xiàn)了我沒有使用 JSX。因為我不想進(jìn)行轉(zhuǎn)譯。現(xiàn)在 Node 和所有流行的瀏覽器都已經(jīng)完全支持 ES6 了,那么作為一個例子來講,讓它直接運行會更好一些。雖然它不能運行在 IE 上,不過也沒關(guān)系,如果是一個真實的線上項目,我會進(jìn)行轉(zhuǎn)譯的。)
還有一個問題是按鍵和展示的邏輯問題,必須要有代碼來控制當(dāng)點擊按鍵的時候發(fā)生什么。這里的按鍵包括數(shù)字鍵(如“1”,“5”)和操作鍵(如“+”,“=”)。按通常的做法,我把組件設(shè)計成了展示型組件(鍵盤)和容器型組件。容器型組件在我的 App 中是唯一包含 state 的組件,它要考慮當(dāng)發(fā)生按鍵行為的時候 App 內(nèi)在邏輯的問題。
“calculator”模塊處理邏輯問題的代碼是一個多帶帶的模塊——calculator。這個模塊對于單元測試是很完美的例子。因為它沒有對 I/O 和 UI 的依賴。你也應(yīng)該盡量使你的應(yīng)用邏輯上保持獨立——模塊不依賴于 I/O 和 UI。
對于 Web 應(yīng)用來講,I/O 是什么?沒有文件和數(shù)據(jù)庫的操作?其實不僅僅是這樣,還有 Ajax 調(diào)用,本地存儲,DOM 操作等,對我而言,任何和瀏覽器 API 有關(guān)的都是 I/O 操作。
我是怎么把計算邏輯從 React 組件中分離出來的呢?其實很簡單,其內(nèi)在邏輯是計算,我把他封裝到一個模塊中就可以了。
這個模塊的實現(xiàn)也很容易——它接收一個計算器 state(一個對象)和一個字符(數(shù)字或者操作符),返回一個新的計算器 state。如果你用過 Redux,它很像 Redux 的 reducer 模式(如果你沒用過 Redux 也沒關(guān)系)。但是如果一直由上一個 state 獲取下一個 state,怎么能回到初始狀態(tài)呢?這里還有一個模塊叫做 initialState,通過它可以初始化計算器。計算器的 state 并不是完全黑盒的,它包含了一個字段叫做 display,可以把你想要展示的 state 顯示在計算器應(yīng)用上。
如果你沒有耐心看源代碼的話,我們一起來看下這里面最重要的部分,應(yīng)用中算法的細(xì)節(jié)其實不重要。
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 } } //....
再次強調(diào)這里的實現(xiàn)細(xì)節(jié)并不重要,重要的是模塊的設(shè)計,它暴露出來的函數(shù)非常簡單——給一個 state,得到下一個 state。
這就是我們在 test-calculator 中所做的事情。那么接下來怎么進(jìn)行測試呢?使用測試框架,目前比較流行的框架是 Mocha ,我們就用它。不過像 Jest,Jasmine,Tape等框架也都行,隨意使用你喜歡的測試框架。
用 Mocha 進(jìn)行單元測試所有的測試框架都類似,寫測試代碼調(diào)用被測函數(shù),通過測試框架運行他們,其中運行它們的代碼通常叫做“runner”。
Mocha runner 叫做 “mocha”,如果你看測試腳本的 package.json,可以看到:
"scripts": { ... "test": "mocha "test/**/test-*.js" && eslint test lib", ... },
它會運行 test 文件夾中所有以 test- 開頭的文件,你可以復(fù)制我的 repo,npm install 后,運行 npm test 自己試試。
(順便提一句,把所有測試都放在測試目錄,并且測試目錄放在 package 的根目錄是一個公認(rèn)的 npm package 約定,如果你不想讓人覺得你不專業(yè)的話,最好還是遵守這一約定。)
運行它,會得到如下輸出:
這里有 14 個測試通過的提示信息,如果沒通過,就會有紅色提示出現(xiàn)。
我們看下面代碼:
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,這里只引入我們需要的函數(shù):describe,it 和 expect。接下來引入我們要測試的模塊 calculator。
準(zhǔn)備開始測試,用 it 函數(shù)來表達(dá):
it("should show initial display correctly", () => { expect(calculator.initialState.display).to.equal("0") })
it 函數(shù)接收一個字符串(用來表示測試結(jié)果)和一個函數(shù)(待測函數(shù))。it 測試不能多帶帶運行,它們必須組成一個測試組。所以如代碼中所示,用 describe 函數(shù)定義測試組,里面包含了若干個 it 函數(shù)。
測試函數(shù)中寫什么呢?可以寫任何想寫的東西,在這個例子中我們測試了初始狀態(tài)所顯示的是不是 0。如果我們自己來實現(xiàn)怎么實現(xiàn)呢,比如可以像如下代碼:
if (calculator.initialState.display !== "0") throw "failed"
對于這個問題,上面代碼也是可以測出來的。但是 expect 包含了很多特性可以使測試變得更簡單,比如可以測試數(shù)組或者對象是否和一個給定的值相等。這就是單元測試的要點,即運行一個函數(shù),或一組函數(shù),檢查其 運行結(jié)果 是否和 預(yù)期結(jié)果 一致。
編寫單元可測的代碼上面的很簡單對吧!其實對于單元測試來講,難的并不是單元測試本身,而是分離代碼的藝術(shù),把代碼盡量分離成單元可測的模塊。單元可測的代碼一般都是不依賴于其他模塊、不依賴于 I/O 的代碼。這是比較困難的,大多數(shù)人都傾向于把邏輯代碼、I/O 代碼和 UI 代碼寫到一起。困難是困難,但不是說做不到,有很多技巧可以使用,比如你的代碼中有一些驗證字段,那么你就可以把驗證代碼組織到一起形成函數(shù),再對這個驗證函數(shù)進(jìn)行測試。
測試代碼是運行在 NodeJS 下的!?注意一個重要的事情——單元測試是在 NodeJS 下運行的!而計算器應(yīng)用是運行在瀏覽器端的,上面的生產(chǎn)代碼都是在 NodeJS 下進(jìn)行測試的,這也可以嗎?
當(dāng)然可以。因為我們的代碼是同構(gòu)的,它可以運行在瀏覽器端和 NodeJS 上。如果你的代碼沒有使用任何 I/O,就是說沒有對瀏覽器做任何的特化處理,那么它就沒有理由不能運行在 NodeJS 上。另外,如果你使用了 require,它既可以被本地的 NodeJS 識別,也可以被像 Webpack 一樣的打包器識別。你看代碼中的 package.json,就可以看到我們就是使用了 Webpack,用 require 進(jìn)行代碼打包:
"scripts": { "build": "webpack && cp public/* dist", ... }
代碼中使用 require 來引入 React 或者其他模塊,這不論是在 NodeJS 中還是瀏覽器中都是通用的。
在瀏覽器中運行單元測試我們還可以使用另一個測試框架,Karma 。使用它可以在瀏覽器中運行 Mocha 代碼,但是這里表達(dá)一下我的淺見:單元測試能在 Node 下運行就在 Node 下運行,因為很容易執(zhí)行和 debug(當(dāng)然現(xiàn)在在瀏覽器中執(zhí)行也很方便)。并且如果代碼不需要轉(zhuǎn)譯的話,執(zhí)行的也非常快。
但是我們的代碼沒有在瀏覽器中測試確實是個問題,因為我們并不真正地知道代碼在瀏覽器中運行會是什么樣子。瀏覽器中的 JS 執(zhí)行環(huán)境和 NodeJS 環(huán)境可能會有微妙的差別。
總結(jié)本文中主要介紹了什么:
介紹了如何使用 Mocha (和 Chai)創(chuàng)建單元測試;
介紹了單元測試就是以代碼單元為單位進(jìn)行測試,這個代碼單元是獨立于其他模塊的。
介紹了設(shè)計模塊時應(yīng)該獨立于其他模塊。如果一定要有依賴,那么可以 mock 一個其他模塊對本模塊進(jìn)行單元測試,或者進(jìn)行集成測試。
介紹了我們測試的代碼單元應(yīng)該是同構(gòu)的,這樣就可以在 NodeJS 環(huán)境下進(jìn)行測試了。
介紹了如何寫同構(gòu)代碼——沒有 I/O操作、使用 require 引入模塊、使用 Webpack 來打包模塊以使其符合瀏覽器運行環(huán)境。
下文簡介下篇文章我們介紹端到端測試,把我們的代碼在真實環(huán)境(瀏覽器)中測試。請看下一篇文章《測試你的前端代碼 - part3(端到端測試)》。
我最近正在寫一本《React.js 小書》,對 React.js 感興趣的童鞋,歡迎指點。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/82184.html
摘要:單元測試上一節(jié)有討論過,單元測試就是以代碼單元為單位進(jìn)行測試,代碼單元可以是一個函數(shù),一個模塊,或者一個類。單元測試是最容易理解也最容易實現(xiàn)的測試方式。在寫單元測試的時候,盡量將你的單元測試獨立出來,不要幾個單元互相引用。 showImg(https://segmentfault.com/img/remote/1460000008823416?w=997&h=350); 本文作者:G...
摘要:測試光譜光譜的一端單元測試顧名思義,代碼以單元為單位進(jìn)行測試。這個系列文章整體如下測試你的前端代碼單元測試測試你的前端代碼端到端測試測試你的前端代碼集成測試。 showImg(https://segmentfault.com/img/remote/1460000008812278?w=998&h=354); 本文作者:Gil Tayar 編譯:胡子大哈 翻譯原文:http://hu...
摘要:測試光譜光譜的一端單元測試顧名思義,代碼以單元為單位進(jìn)行測試。這個系列文章整體如下測試你的前端代碼單元測試測試你的前端代碼端到端測試測試你的前端代碼集成測試。 showImg(https://segmentfault.com/img/remote/1460000008812278?w=998&h=354); 本文作者:Gil Tayar 編譯:胡子大哈 翻譯原文:http://hu...
摘要:單元測試幾乎不會出現(xiàn)不穩(wěn)定的情況,因為單元測試通常是簡單輸入,簡單輸出。鏈接直達(dá)測試你的前端代碼集成測試。 本文作者:Gil Tayar 編譯:胡子大哈 翻譯原文:http://huziketang.com/blog/posts/detail?postId=58d50da37413fc2e8240855c 英文連接:Testing Your Frontend Code: Part ...
摘要:單元測試幾乎不會出現(xiàn)不穩(wěn)定的情況,因為單元測試通常是簡單輸入,簡單輸出。鏈接直達(dá)測試你的前端代碼集成測試。 本文作者:Gil Tayar 編譯:胡子大哈 翻譯原文:http://huziketang.com/blog/posts/detail?postId=58d50da37413fc2e8240855c 英文連接:Testing Your Frontend Code: Part ...
閱讀 2556·2021-09-22 15:25
閱讀 2963·2021-09-14 18:03
閱讀 1211·2021-09-09 09:33
閱讀 1699·2021-09-07 09:59
閱讀 2930·2021-07-29 13:50
閱讀 1500·2019-08-30 15:44
閱讀 1715·2019-08-29 16:22
閱讀 1287·2019-08-29 12:49