vue 單元測試初探
前言
單元測試(unit testing),是指對軟件中的最小可測試單元進行檢查和驗證。對于單元測試中單元的含義,一般來說,要根據(jù)實際情況去判定其具體含義,如C語言中單元指一個函數(shù),Java里單元指一個類,圖形化的軟件中可以指一個窗口或一個菜單,在vue、react、angular等前端框架中,最重要的是針對組件的單元測試
為什么要引進單元測試?
現(xiàn)今時代,各種編程語言,開發(fā)框架,集成工具蓬勃發(fā)展,然而軟件工程師們卻仍然掙扎在第一線,被bug、遺留代碼、技術(shù)債務、重構(gòu)搞得焦頭爛額,當你的項目足夠大的時候,在疊加模塊和組件的過程中,是很有可能影響之前的模塊。但是被影響的模塊已經(jīng)通過了測試,我們在迭代的時候,很少有測試人員會去重新測試這個系統(tǒng)。所以, 被影響的模塊很可能就有了一個隱形的bug被部署到線上。因此我們采用自動化測試。最主要的作用是對于大型項目,在每次迭代的時候, 可以保證整個系統(tǒng)的正確運行, 確保系統(tǒng)的健壯,總結(jié)以下幾點:
- 自動化的測試,節(jié)省時間
- 減少低級的bug
- 提供了對組件行為描述的文檔
- 能在編寫單測中改進代碼
- 利于閱讀組件,促進重構(gòu)
- 證明你的工作已完成
- 利于代碼評審
- 代碼性能
- 提供一些度量指標
單元測試概述
單元測試通常是針對應用程序中最小的部分,在vue中組件是要測試的單元(后面介紹)
首先,我們來開始一個簡單的單元測試,在代碼中,使用sum函數(shù)計算兩數(shù)之和。
單元測試是在源代碼中單獨調(diào)用函數(shù)并斷言其行為正確的函數(shù),看看如下的例子,這是一個比較簡單的程序,導出一個sum函數(shù),然后運行該函數(shù),斷言其如果不返回則拋出錯誤。
// sum.js export default function sum(a,b){ return a + b } // sum.spec.js import sum from './sum.js' function testSum(){ if(sum(1,2)!==3){ throw new Error('sum(1,2) not return 3') } } testSum()
因為單元測試是針對隔離的單元進行測試的,所以當編寫出良好的單元測試,能準確的將代碼問題暴露出來。
另外在測試中,我們可能會關(guān)注快照測試,快照測試類似于差異發(fā)現(xiàn),快照測試將運行的程序進行截圖比較,如果存在差異,則會顯示錯誤,在vue測試中,vueTestUtil提供了類似的能力,可以比較js中可序列化的值,在組件中就是比較dom輸出
測試開發(fā)的模式
如果你關(guān)注測試開發(fā)方面的知識,你應該聽說過測試驅(qū)動開發(fā)(TDD - Test Driven Development)以及行為驅(qū)動開發(fā)(BDD - Behavior Driven Development)
1. 測試驅(qū)動開發(fā)(TDD - Test Driven Development)
測試驅(qū)動開發(fā),英文全稱Test-Driven Development,簡稱TDD,是一種不同于傳統(tǒng)軟件開發(fā)流程的新型的開發(fā)方法。它要求在編寫某個功能的代碼之前先編寫測試代碼,然后只編寫使測試通過的功能代碼,通過測試來推動整個開發(fā)的進行。這有助于編寫簡潔可用和高質(zhì)量的代碼,并加速開發(fā)過程
首先,開發(fā)人員在編寫業(yè)務邏輯之前,先編寫一些測試用例 如果運行這些測試用例,會得到失敗的結(jié)果,因為我們根本沒有實現(xiàn)要測試的業(yè)務邏輯 實現(xiàn)這些業(yè)務邏輯 運行測試用例,查看通過率,如果你是一個優(yōu)秀的開發(fā)者,可能這些用例都可以通過 修復測試用例,或者重構(gòu)
當我們開發(fā)新功能時,依然是重復上述步驟,核心就是測試用例前置,流程圖如下:
舉個栗子: 我們通過具體的實例來描述一下TDD,假設我們現(xiàn)在的需求是實現(xiàn)一個階乘函數(shù),我們使用jest來實現(xiàn)這個測試用例
var fac = require("../src/index.js"); test("輸入負數(shù),應該返回NaN ", () => { expect(fac(-1)).toBe(NaN); }); test("輸入0,應該返回1 ", () => { expect(fac(0)).toBe(1); }); test("輸入1,應該返回1 ", () => { expect(fac(1)).toBe(1); }); test("輸入2,應該返回2", () => { expect(fac(2)).toBe(2); }); test("輸入3,應該返回6", () => { expect(fac(3)).toBe(6); });
運行這個測試用例,肯定會失敗,因為我們還沒有實現(xiàn)這個fac函數(shù),接下來我們來實現(xiàn)這個階乘函數(shù)
module.exports = function fac(n) { if (n < 0) return NaN; if (n === 0) return 1; return n * fac(n - 1); };
現(xiàn)在我們再次運行這個測試用例,得到如下結(jié)果:
可以看到,所有的case都通過了,這個就是TDD的開發(fā)模式
2. 行為驅(qū)動開發(fā)(BDD - Behavior Driven Development)
在傳統(tǒng)軟件開發(fā)中,業(yè)務人員拿到需求,將需求交給需求分析人員,需求分析人員編寫需求說明書或者設計,然后軟件開發(fā)人員根據(jù)需求說明進行架構(gòu)設計和代碼開發(fā),接著測試人員根據(jù)需求說明編寫測試用例進行測試,從需求產(chǎn)生到測試交付,有多個不同角色的參與,期間很容易產(chǎn)生信息丟失與理解偏差,其中只要出現(xiàn)一個環(huán)節(jié)錯誤,研發(fā)團隊就很難交付合格的產(chǎn)品。
BDD是一種敏捷軟件開發(fā)的技術(shù),它鼓勵軟件中的開發(fā)者、QA和非技術(shù)人員或商業(yè)參與者之間的協(xié)作,特別適用于敏捷項目
舉個栗子描述一下:
var fac = require("../src/index.js"); describe("驗證階乘函數(shù) fac:", function () { it("輸入負數(shù),應該返回NaN ", () => { expect(fac(-1)).toBe(NaN); }); it("輸入0,應該返回1 ", () => { expect(fac(0)).toBe(1); }); it("輸入1,應該返回1 ", () => { expect(fac(1)).toBe(1); }); it("輸入2,應該返回2", () => { expect(fac(2)).toBe(2); }); it("輸入3,應該返回6", () => { expect(fac(3)).toBe(6); }); });
運行測試用例,得到結(jié)果:
對比代碼內(nèi)容與測試結(jié)果,發(fā)現(xiàn)差別并不大,最主要的區(qū)別就是措辭的區(qū)別,BDD的測試用例看起來就像是在看一篇文檔一樣,結(jié)構(gòu)十分清晰,對于團隊配合,代碼閱讀,促進重構(gòu)有著不可小覷的作用,當你可以流暢的閱讀測試用例的時候,自然也能編寫出更好的代碼。
這里的例子只是描述與測試驅(qū)動開發(fā)的差別,并不能代表真正的行為驅(qū)動開發(fā),行為驅(qū)動開發(fā)更像是一種概念理論
總結(jié):BDD更加注重功能的而不只是關(guān)注結(jié)果,另外借用一句業(yè)內(nèi)名言:BDD幫助開發(fā)人員設計(design)軟件,TDD幫助開發(fā)人員測試(test)軟件。
Vue中的單元測試
單元測試允許你將獨立單元的代碼進行隔離測試,其目的是為開發(fā)者提供對代碼的信 心。通過編寫細致且有意義的測試,你能夠有信心在構(gòu)建新特性或重構(gòu)已有代碼的同時,保持應用的功能和穩(wěn)定。 為一個 Vue 應用做單元測試并沒有和為其它類型的應用做測試有什么明顯的區(qū)別。
框架選擇
如果你是Vue的開發(fā)者,你應該十分了解vue的組件中template的寫法,template、style、script模板式的語法,相對于React中Jsx語法更加直接和自然,Vue中將組件作為最小測試單元十分合適。
雖說單元測試通常與框架并沒有直接關(guān)系,但是對于功能集合、性能和對單文件組件預編譯的支持、單測產(chǎn)生價值以及開發(fā)過程的便捷性你需要對其進行評估。
一流的錯誤報告
當測試失敗時,提供有用的錯誤信息對于單元測試框架來說至關(guān)重要。這是斷言庫應盡的職責。一個具有高質(zhì)量錯誤信息的斷言能夠最小化調(diào)試問題所需的時間。除了簡單地告訴你什么測試失敗了,斷言庫還應額外提供上下文以及測試失敗的原因,例如預期結(jié)果 vs. 實際得到的結(jié)果。 一些諸如 Jest 這樣的單元測試框架會包含斷言庫。另一些諸如 Mocha 需要你單獨安裝斷言庫 (通常會用 Chai)。
活躍的社區(qū)和團隊
因為主流的單元測試框架都是開源的,所以對于一些旨在長期維護其測試且確保項目本身保持活躍的團隊來說,擁有一個活躍的社區(qū)是至關(guān)重要的。額外的好處是,在任何時候遇到問題時,一個活躍的社區(qū)會為你提供更多的支持。
這里我們綜合考慮使用Jest框架,Jest 是一個專注于簡易性的 JavaScript 測試框架。一個其獨特的功能是可以為測試生成快照 (snapshot),以提供另一種驗證應用單元的方法。
Jest 是功能最全的測試運行器。它所需的配置是最少的,默認安裝了 JSDOM,內(nèi)置斷言且命令行的用戶體驗非常好。
Jest資料
Vue官方提供了十分便捷的測試工具庫:Vue Test Utils,接下來會講解如何使用 Vue Test Utils 對vue的組件進行單元測試。
Vue Test Utils
它提供了豐富的API,擁有渲染組件實例,選擇器、模擬插入全局組件、模擬狀態(tài)、數(shù)據(jù)流、生命周期、事件等強大的功能,甚至可以模擬路由等,接下來我們來嘗試一下。
安裝:
安裝Vue Test Utils的方式并不難,我們先選擇一個測試運行器,可以選擇 Jest或者Mocha,這里我們選擇Jest。
如果您還沒有使用Vue-cli創(chuàng)建項目,可以在vue-cli創(chuàng)建項目時選擇Jest,框架會自動安裝Vue Test Utils,運行:
vue create vue-test
如果您已經(jīng)有通過Vue-cli創(chuàng)建的項目,則可以運行:
vue add @vue/unit-jest
配置Jest: jest的配置可以放到根目錄的jest.config.js 或者 jest.config.json里面
module.exports = { preset: "@vue/cli-plugin-unit-jest", // 單測插件 moduleFileExtensions: ["js", "json", "vue", "less", "css"], // 后綴 transform: { // 模塊解析 "^.+\\.js$": "<rootDir>/node_modules/babel-jest", ".*\\.(vue)$": "<rootDir>/node_modules/vue-jest", }, moduleNameMapper: { // 別名識別 "^@/(.*)$": "<rootDir>/src/$1", "\\.(css|less)$": "<rootDir>/tests/mocks/styleMock.js", }, // 快照解析 需要安裝 jest-serializer-vue snapshotSerializers: ["<rootDir>/node_modules/jest-serializer-vue"], collectCoverage: true, // 覆蓋率目錄 coverageDirectory: "report/coverage", // 單測報告配置,需要安裝jest-html-reporter reporters: [ "default", [ "./node_modules/jest-html-reporter", { logo: "https://rdc-test.mingyuanyun.com/static/img/rdc.png", pageTitle: "單測報告(工作臺)", outputPath: "report/unit-test/index.html", includeFailureMsg: true, }, ], ], };
需要安裝的模塊:
- jest-serializer-vue(序列化工具)
- jest-html-reporter(單測報告工具,也可以選擇其他工具)
配置完成后,我們就可以愉快地運行單測啦
如下所示,這是一個十分簡單的點擊數(shù)字自增的組件:
// Increment.js <template> <div> <p>number is {{ count }}</p> <button @click="increment">increment</button> </div> </template> <script> export default { data() { return { count: 0, }; }, methods: { increment() { this.count++; }, }, }; </script> <style scoped lang="less"> p { font-size: 2em; text-align: center; } </style>
Vue Test Utils提供了實現(xiàn)包裹器的方法,mount,shallowMount,獲取包裹器后,我們可以開始使用其實例上封裝的眾多接口
// increment.spec.js // 導入測試工具集 import { mount } from "@vue/test-utils"; import Increment from "@/views/Increment"; describe("Increment", () => { // 掛載組件,獲取包裹器 const wrapper = mount(Increment); const vm = wrapper.vm; it("render markup", () => { expect(wrapper.html()).toContain("<p>number is 0</p>"); }); // 模擬用戶點擊 it("button click should increment the count", () => { expect(vm.count).toBe(0); const button = wrapper.find("button"); button.trigger("click"); expect(vm.count).toBe(1); }); // 點擊后查看dom it("button click should increment the count and update the dom", async () => { expect(wrapper.text()).toContain("1"); const button = wrapper.find("button"); await button.trigger("click"); expect(wrapper.text()).toContain("2"); }); });
寫好單元測試后,我們來執(zhí)行一下:
npm run test:unit
運行完成后,在我們項目根目錄,可以查看到單測報告 report/unit-test/index.html ,在瀏覽器打開,就可以查看
打開coverage/lcov-report/index.html可以查看覆蓋率
好了,我們現(xiàn)在已經(jīng)完成一個簡單的單元測試用例了,我們使用了掛載、包裹器、選擇器,事件觸發(fā)器等,具體還有很多的API可以查看官方文檔
文檔
以上就是vue 單元測試初探的詳細內(nèi)容,更多關(guān)于vue 單元測試的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue+iview 兼容IE11瀏覽器的實現(xiàn)方法
這篇文章主要介紹了vue+iview 兼容IE11瀏覽器的實現(xiàn)方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01vue axios數(shù)據(jù)請求get、post方法及實例詳解
axios是一個基于Promise,同時支持瀏覽器端和Node.js的HTTP庫,常用于Ajax請求。這篇文章主要介紹了vue axios數(shù)據(jù)請求get、post方法的使用 ,需要的朋友可以參考下2018-09-09解決VUE項目localhost端口服務器拒絕連接,只能用127.0.0.1的問題
這篇文章主要介紹了解決VUE項目localhost端口服務器拒絕連接,只能用127.0.0.1的問題2020-08-08