前端測試:Part II (單元測試)

原文:Testing Your Frontend Code: Part II (Unit Testing)html

By Gil Tayar前端

單元測試

咱們在Part1裏已經說過,但與那測試就是測試單元的代碼,無論這些單元是函數、模塊仍是類。多數人認爲測試應該以單測爲主,但我不這麼認爲,若是你贊成也沒有問題。我會一遍一遍又一遍地在這一系列文章中強調,你怎測試都行,只要你寫了足夠多的測試,讓你對你的上線有信心就行。webpack

無論你寫多少單測,單測確實是最好寫也最好懂的測試,它們天生具備函數屬性。設定一個單元的輸入,執行,檢查輸出(輸入可能就是一個函數的參數,輸出就是返回值)。git

更重要的是你應該提醒本身寫代碼時要讓這些單元彼此隔離,不會相互依賴,這樣才能方便單測。github

計算器應用中的單元

理論已經夠多了,咱們看下咱們的計算器,源碼在這裏。這是個React應用,有兩個主要的組件,keypaddisplay。顯然他們是單元且對其餘單元無依賴。可是他們是React層面的單元,之後我會專門講如何測它們。web

若是你已經閱讀過代碼可能會疑問爲啥我不用JSX。緣由是我不想使用代碼轉譯。Node和現代的瀏覽器可以識別ES6,因此爲啥不讓你寫的代碼直接運行呢?是的,個人代碼不能再IE中運行,可是這只是個demo,因此沒問題。在一個真實的項目裏,我會添加代碼轉譯的。算法

譯註:最新的代碼倉庫已經用JSX重構並增長了babel轉譯,因此這段呵呵了。數據庫

總有些代碼負責處理當點擊了一個數字或者運算符後的運算,哪部分代碼完成這部分工做呢?按照如今時髦的作法,我也把個人組件分紅了展現型組件(keypaddisplay)和狀態型組件(calculator-app)。後一個組件時惟一一個有狀態的組件,負責調用計算邏輯,並驅動display展現點擊後應該顯示啥。npm

calculator模塊

負責計算的邏輯並不在組件中,它是一個單獨的模塊calculator,並不依賴React。這樣的模塊是最適合單測的。對UI和IO沒有依賴的模塊最適合單測。你應該盡力讓你更多的業務邏輯以不依賴IO或者UI的形式寫成模塊。json

在前端裏,不依賴IO是啥意思呢?不訪問文件、數據庫… ?不,前端裏原本就沒有這些。可是還會有Ajax,local storage,DOM 訪問,瀏覽器API訪問,對我而言,這些都是IO。

我是如何把計算器的邏輯和組件分開的?在這裏,其實很簡單,這些邏輯都是算法類的,我把它們放到了calculator模塊中。

這個模塊很是簡單,輸入是計算器的當前狀態(一個對象)和一個字符(一個數字或者運算符),返回值是計算器的新狀態。若是你用過Redux,這個邏輯跟Redux的Reducer差很少。可是如何獲取最原始的狀態呢?簡單,這個模塊一樣Export了initialState,你能夠用它去初始化計算器。計算器的狀態並不是不透明的,它包含可一個組件display,用來將計算器的內在狀態顯示出來。

若是你沒有耐心去仔細閱讀代碼,咱們這裏只看下開頭就行,這部分最重要,算法是怎麼實現的其實可有可無。

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
  }
}

//....
複製代碼

怎麼測試呢?咱們一般會用一個測試框架。當下最流行的測試框架是Mocha,咱們就用它來測試。固然用Jest,Jasmine,Tape或者其餘框架均可以。

用Mocha進行單測

全部的測試框架都是相似的——你把測試代碼寫成函數,測試框架負責執行它們。

npm installMoch後,咱們就能夠經過npm腳本運行了。固然,命令就是"Mocha"。看下package.json,你會看到:

"scripts": {
...
    "test": "mocha 'test/**/test-*.js' && eslint test lib",
...
},
複製代碼

運行npm test,就會執行以test打頭的文件夾裏的測試腳本。若是你clone了這個代碼倉庫,你要先npm install

(順便說下,把測試腳本放在根目錄下的test文件夾中是測試的慣例,若是你想要讓別人認爲你寫測試很專業,那你也應該這麼作)

執行後,輸出長這個樣子。

若是有一個測試沒有經過,你會看到刺眼的紅色,而後就能夠立刻修改了。

看一下咱們的測試用例:

// test-calculator.js
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(稍後咱們會將啥事斷言庫)。引入一些咱們須要的函數describeit

而後引入咱們要測試的模塊——calculator

而後就是使用it函數定義的測試用例了,以下:

it('should show initial display correctly', () => {
    expect(calculator.initialState.display).to.equal('0')
})
複製代碼

it函數接受一個字符串參數,用來描述測試用例,另外一個參數是一個函數,就是測試自己了。可是it是不能「裸奔」的,須要被包裹在describe函數定義的測試組中。

在測試邏輯中寫啥呢?其實啥均可以。在這裏咱們就是判斷了下初始展現的值是否是等於0. 若是不用expect,咱們能夠這麼寫:

if (calculator.initialState.display !== '0')
  throw 'failed'
複製代碼

Mocha中若是一個測試不經過,就會拋出一個異常,就是這麼簡單。可是使用expect讓咱們可使用他的一些特性來方便地檢查數組、對象的值。

這就是單元測試的主旨了——執行一個或者一組函數(若是你是面向對象測試,則一般實例化一個對象,而後調用它的方法),檢查返回的結果是否等於預期結果。

編寫可測試的代碼

單測裏最複雜的不是測試自己,而是分離代碼,從而讓它們儘量地變得可測。**可單測的代碼就是,對其餘模塊和IO沒有依賴的代碼。**這並不簡單,由於咱們一般習慣於講業務邏輯和IO,UI耦合起來。可是這個目標仍然是能夠達到的,有不少技術。例如,若是你有一段驗證表單的代碼,把它們分離出來變成一個一個的驗證函數,而後對它們進行測試。

測試代碼運行在Node環境下?

注意到很重要的一點——單測是運行在Node環境下的。即便計算器應用的代碼是跑在瀏覽器中,咱們仍然使用Node去跑咱們的測試,包括要上線的代碼。

這怎麼能行呢?這是由於咱們的代碼是同構的。這意味着它們能同時運行在瀏覽器和Node環境中。若是你的代碼中沒有任何IO操做,這就意味着它並非只能在瀏覽器中運行。尤爲是,咱們的代碼使用了require來組織代碼,既能被NodeJS識別,又能被Webpack打包(看下package.json,你就會發現使用了webpack)。

"scripts": {
   "build": "webpack && cp public/* dist",
   ...
}
複製代碼

在瀏覽器環境下單測

順便提一下,咱們可使用karma來實如今瀏覽器中運行Mocha。可是我謹認爲若是能在Node中運行就在Node中運行(如今你其實很容易寫出在兩端都能運行的代碼),由於不管是運行仍是debug都更方便。不編譯代碼的話,運行起來會更快。

可是不在瀏覽器中跑測試,咱們就不能確認咱們的代碼能在瀏覽器中運行。兩個環境中的一些細微差異可能會致使一些問題。

下週

上面說的問題就是E2E測試要管的事了——在真實的瀏覽器環境中測試咱們的代碼。下週咱們將如何寫E2E測試。

()

相關文章
相關標籤/搜索