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