也來扯扯 Vue 單元測試

file
從使用 Vue 寫出第一個 Hello world 到如今已經有近兩年時間了,期間利用業餘時間折騰了一套組件 we-vue,起初是出於實踐學到的新知識,更多的是玩的意思,不事後來維護的過程當中漸漸積累了一些經驗,並開始享受這種過程。css

在 we-vue 更新到 v2.0 的時候,開始全面地編寫單元測試。起先使用 karma + mocha + chrome-headless 這種組合完成的行級覆蓋率達到 96% 的測試。但最近,我又放棄了這種組合,轉而使用 Jest。在這連番的折騰中,入過很多坑(固然,不少時候是本身挖坑本身跳),也解鎖了很多新姿式。前端

本文主要扯一扯本身在完成這些單元測試,以及遷移到 Jest 過程當中的一些收穫。文中並不會涉及很是具體的測試寫法,由於這些教程官方文檔已經作得很好了。但願文中的一些內容對於正準備作 Vue (其實也不只限於 Vue) 單元測試的人能有所幫助。vue

爲何要作單元測試

做爲一個程序員,單元測試或許是一個繞不開的坎。我大體總結了一下當初決定作單元測試的緣由:node

  1. 有單元測試的項目,顯得更專業,更有 B 格。真的,認真回憶下,這確實是當初本身的首要動機,可能這算是動機不純吧?
  2. 懶!受不了每次調整以後,得不斷地檢查代碼,甚至查看頁面源碼是否符合預期。不斷修改各類參數並刷新以測試不一樣狀況下的結果。而這裏面的一大部分工做其實可讓單元測試來完成。因此說,懶人讓世界更美好!
  3. 單元測試能避免出現一些代碼運行結果與預期不符的錯誤,一般是一些比較低級但又難以發現的問題。
  4. 單元測試可以避免在升級更新、修復 BUG 的時候引入一些意料以外的問題。有時候自覺得小修改小優化無大礙,其實否則!
  5. 單元測試對提升代碼質量頗有幫助。由於,好的代碼通常是便於測試的。若是在進行單元測試過程當中發現本身的一些代碼不方便進行測試,那麼你可能須要從新審視這些代碼,看是否有一些設計上不合理或者能夠優化的地方。固然,這也並非說代碼應該「遷就」於單元測試,若是這樣就有點兒本末倒置了。

總之,單元測試能提升程序的可靠性,讓開發者在發佈時更有底氣,讓使用者更有安全感。雖然編寫單元測試須要花費一些時間,但相比於它所帶來的優點,這些時間和精力上的花費仍是值得的。git

另外值得注意的是,單元測試並不能徹底代替功能測試,由於程序自己設計的邏輯錯誤或者其它的一些環境因素所形成的影響,單元測試可能無能爲力。因此,單元測試只是保證你想讓程序模塊輸出一隻豬,它不會整出一頭驢來。至於進一步的功能測試或者說「肉測」,仍然是有必要的。程序員

用到的一些工具(軟件)

趁手的傢伙 -- WebStorm / Visual Studio Code

去打羣架,得操個趁手的傢伙,板磚極好,大木頭棒子也不錯。github

而寫代碼,一個好用的 IDE 或者編輯器,能讓效率飛昇。就我我的而言,作前端時大部分時間使用 WebStorm,其自己對 Vue.js 就有很好的支持(內置了相關的插件)同時也支持的各類測試框架,適當的配置以後,能夠很方便的進行斷點、查看規模之類的調試工做。chrome

此外,Visual Studio Code 也是個不錯的選擇,目前已不有少 Vue.js 開發和測試相關的插件了,只須要搜索加點擊但可安裝。vue-cli

固然,這裏我無心挑起各類 IDE 或者編輯器流派之爭端,提到的兩個只是本身我的喜愛。你們可自行選擇合適的,甚至只要本身喜歡且以爲方便,用記事本開幹也沒問題。

file

用成熟好用的測試工具庫 -- vue-test-utils

vue-test-utils 是 Vue 生態圈中的一個開源項目,其前身是 avoriaz,avoriaz 也是一個不錯的包,但其 README 中有說明,當 vue-test-utils 正式發佈的時候, 它將會被廢棄。shell

vue-test-utils 能極大地簡化 Vue.js 單元測試。

例如,網上一搜 Vue 單元測試,獲得的例子通常是像下面這樣的(包括 vue-cli 提供的模板裏默認也是這樣):

import Vue from 'vue'
import HelloWorld from '@/components/HelloWorld'

describe('HelloWorld.vue', () => {
  it('should render correct contents', () => {
    const Constructor = Vue.extend(HelloWorld)
    const vm = new Constructor().$mount()
    expect(vm.$el.querySelector('.hello h1').textContent)
      .toEqual('Welcome to Your Vue.js App')
  })
})

使用 vue-test-utils 後,你能夠像下面這樣

import { shallow } from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld'

describe('HelloWorld.vue', () => {
  it('should render correct contents', () => {
    const wrapper = shallow(HelloWorld, {
      attachToDocument: ture
    })

    expect(wrapper.find('.hello h1').text()).to.equal('Welcome to Your Vue.js App')
  })
})

能夠看到代碼更加簡潔了。wrapper 內含許多有用的方法,上面的例子中所使用的 find() 其中最簡單不過的一個。vue-test-utils 還有 createLocalVue() 等方法以及 stub 之類的功能,基本上能夠完成絕大部分狀況下的測試用例。

須要注意的是,截至日前(2018-03-21)仍然處於 Beta 階段。在正式版發佈以前可能會有大的更改,例如新增或廢棄一些方法。同時也可能存在一些 BUG(本身就曾 修復過一個 )。但目前整體來講已趨於穩定,推薦使用,須要留意其最新更改。

選擇一個好用的斷言庫

一般是 chai,有時候結合 sinon 一塊兒使用。chai 是一個優秀的庫,裏面的方法十分完善。網上相關的教程更是不可勝數,這也反映出它很受歡迎。

不過我的並不太中意 chai 的語法,好比比較經常使用的 to.be.ok,to.not.be.ok,expect({a: 1, b: 2}).to.be.an('object').that.has.all.keys('a', 'b'),爲啥要那麼長?爲啥要那麼多句點?順序忘了怎麼弄?

file

因此一開始我就選擇了 expect.js (expect 是 Jest 的一部分,能夠單獨安裝使用),主要是它的語法更符合個人口味,這也爲後期遷移到 Jest 省了很多事。

一個合適測試框架 -- Jest

這裏只提到了 Jest,固然也是我的喜愛而已,這也是本身最終決定的方案。固然此前使用的 karma + mocha + chai + chrome... 那一套也有其適用場景和可取之處。後面將會提到 Jest 的一些優勢和缺點。

利用 CI 服務自動進行單元測試、構建以及發佈

如今已經有很多平臺提供 CI 服務,例如 TravisCI 和 CircleCI。對於開源的項目,能無償使用這些平臺的服務持續集成一些平常構建、測試工做。

本身目前使用 CircleCI,具體緣由就很少說了,使用哪一個取決於自身喜愛和具體業務狀況,甚至能夠考慮本身搭建 CI 服務器。

爲本身的項目加入測試覆蓋率徽標

在本身開源項目的 README 中加入一個顯示單元測試覆蓋率的徽標,會增進用戶的第一印象。高覆蓋率的徽標,會使項目顯得更專業可靠,也能讓用戶進一步瞭解整個項目並最終選用。

CodeCov 能提供這種服務,並能夠結合前面提到的 CI 使用,經過 CI 在代碼推送後自動執行單元測試,經過後將代碼覆蓋率相關數據發送給 CodeCov,這樣,在 README 中加入的覆蓋率徽標就能自動更新了。

爲此,你須要一個 codecov 帳號(一般用 GitHub 帳號登陸便可)並安裝 codecov

$ yarn add -D codecov

而後在 CI 的任務配置里加入上傳代碼測試覆蓋率數據的步驟,例如 CircleCI 配置以下:

steps:
    ## 前面部分任務省略

    # run tests!
    - run: yarn test

    # update codecov stats
    - run: ./node_modules/.bin/codecov

最後在 README 里加入微標圖片就能夠了,就像這樣。

code cov

Jest 相對於 karma + mocha + Chrome 組合的優缺點

前面提到,我最終轉向了使用 Jest,這並不是一時腦熱,而是通過屢次權衡和嘗試以後才做的決定。主要是因爲 Jest 相對於以前的方案有着很多優點,一些特性讓測試變得更輕鬆愉快,更有效率。

我大體作了下對比,粗略總結以下:

優勢

  1. 一站式的解決方案

    在使用 Jest 以前,我須要一個測試框架(mocha),須要一個測試運行器(karma),須要一個斷言庫(chai),須要一個用來作 spies/stubs/mocks 的工具(sinon 以及 sinon-chai 插件),一個用於測試的瀏覽器環境(能夠是 Chrome 瀏覽器,也能夠用 PhantomJS)。

    而使用 Jest 後,只要安裝它,全都搞定了。

  2. 全面的官方文檔,易於學習和使用

    Jest 的官方文檔很完善,對着文檔很快就能上手。而在以前,我須要學習好幾個插件的用法,至少得知道 mocha 用處和原理吧 我得學會 karma 的配置和命令,chai 的各類斷言方法……,常常得周旋於不一樣的文檔站之間,實際上是件很煩也很低效的事。

  3. 配置簡單方便
  4. 更直觀明確的測試信息提示
  5. 方便的命令行工具

    全局安裝 Jest 後,能夠在命令行執行單元測試,配合各類命令參數,能夠方便地實現執行單個測試、監視文件變化並自動執行等功能。特別是對於監視文件變化並執行,它提供多種模式,能夠只執行修改過的測試。記得初次讀到這部分文檔時,我不由仰天長嘯,居然有這麼騷氣凌人的操做?

    Jest 甚至提供了 jest-codemods 這一工具,用來將使用其它包的測試遷移爲使用 Jest

缺點

  1. jsdom 的一些侷限性

    由於 Jest 是基於 jsdom 的,jsdom 畢竟不是真實的瀏覽器環境,它在測試過程當中其實並不真正的「渲染」組件。這會致使一些問題,例如,若是組件代碼中有一些根據實際渲染後的屬性值進行計算(好比元素的 clientWidth)就可能出問題,由於 jsdom 中這些參數一般默認是 0.

    因此有些狀況下,測試中可能要施以一些騷操做,好比自行 mock(實例上就是僞造,但合理地僞造)一些中間值,來知足測試用例。若是你的項目中這樣的狀況不少,仍是建議使用 karma + mocha + chrome 這一組合。

  2. 周邊相關的包可能還不完善

    例如 vue-jest,目前的版本並不能徹底實現 vue-loader 的功能。好比,使用 sass,postcss 之類的功能,它會拋出警告信息。代碼中直接 import 實際的 css 文件,則有可能報錯,這時則須要使用 mock 來模擬 css 文件。這些問題,在使用 karma-mocha Chrome 的時候是沒有的,由於測試運行於真實的瀏覽器環境中。

ChromeHeadless vs. PhantomJS?

較新版本的 Chrome 支持以 headless 模式運行,這對於測試這種不須要顯示界面的任務來講是很合適了(其實也可使用常規模式,只不過執行測試的時候 Chrome 會彈出窗口)。

而在 Chrome 推出 headless 模式功能以前。咱們一般用 PhantomJS 的 headless WebKit 環境來進行測試,但它有着一些久未解決的問題,並且更新進度愈來愈慢。不欠前(2018-03-05),由於開發組內部意見不合,PhantomJS 項目已經封存了代碼暫停開發了。

Chrome headless 對於 PhantomJS 來講算是一個致命的打擊,特別是
Chrome 官方推出的 puppeteer 在短期內已經被普遍接受和使用。但其實 PhantomJS 仍是有一些適用場景的,例如一些服務器並不支持 Chrome,這種狀況下 PhantomJS 就有用武之地了。不過目前看來,對手的碾壓以及自身維護團隊的渙散,讓我有理由放棄它了。

後記

實踐老是最有效率的學習方式,不停地折騰才能不斷的進步,特別是對於編程這事上,天天都有新的東西出現。

編寫單元測試可能比較枯燥,由於它並不像作新功能同樣讓人興奮。但只要耐心調試,當所有測試用例都經過,當最後測試覆蓋率慢慢提高時,那種成就感也不亞於開發出了新功能!

廣告

最後,無恥地爲本身的 we-vue 打個小廣告 ,雖然目前不成氣候,也還有很多須要完善的地方。但本身會作下去,也但願能有更多的人支持和參與。裏面能夠看到一些覺的組件測試套路,目前組件部分的單元測試覆蓋率已經超過 99%。

項目地址: https://github.com/tianyong90/we-vue
在線文檔: https://wevue.org
在線示例: https://demo.wevue.org

相關文章
相關標籤/搜索