- {{news.collect ? "已收藏" : "收藏"}}
- 轉(zhuǎn)發(fā)
- 評(píng)論
- {{news.like ? "取消贊" : "贊"}}
摘要:在前端進(jìn)階之路前端架構(gòu)設(shè)計(jì)測(cè)試核心這邊文章中通過(guò)分析了傳統(tǒng)手工測(cè)試的局限性去引出了測(cè)試驅(qū)動(dòng)開(kāi)發(fā)的理念并介紹了一些測(cè)試工具這篇文章我將通過(guò)一個(gè)的項(xiàng)目去講解如何使用且結(jié)合官方推薦的去進(jìn)行單元測(cè)試的實(shí)戰(zhàn)一安裝我為本教程寫(xiě)一個(gè)示例庫(kù)您可以直接
在《前端進(jìn)階之路: 前端架構(gòu)設(shè)計(jì)(3) - 測(cè)試核心》這邊文章中, 通過(guò)分析了"傳統(tǒng)手工測(cè)試的局限性" 去引出了測(cè)試驅(qū)動(dòng)開(kāi)發(fā)的理念, 并介紹了一些測(cè)試工具. 這篇文章我將通過(guò)一個(gè)Vue的項(xiàng)目, 去講解如何使用mocha & karma, 且結(jié)合vue官方推薦的vue-test-utils去進(jìn)行單元測(cè)試的實(shí)戰(zhàn).
一. 安裝我為本教程寫(xiě)一個(gè)示例庫(kù), 您可以直接跳過(guò)所有安裝過(guò)程, 安裝依賴(lài)后運(yùn)行該示例項(xiàng)目:
如果想一步步進(jìn)行安裝, 也可以跟著下面的步驟進(jìn)行操作:
(一) 使用腳手架初始化vue項(xiàng)目(使用webpack模板)//命令行中輸入(默認(rèn)閱讀該文章的讀者已經(jīng)安裝vue-cli和node環(huán)境) vue init webpack vueunittest
注意, 當(dāng)詢(xún)問(wèn)到這一步Pick a test runner(Use arrow keys)時(shí), 請(qǐng)選擇使用Karma and Mocha
接下來(lái)的操作進(jìn)入項(xiàng)目npm install安裝相關(guān)依賴(lài)后(該步驟可能更會(huì)出現(xiàn)PhantomJS這個(gè)瀏覽器安裝失敗的報(bào)錯(cuò), 不用理會(huì), 因?yàn)? 之后我們不使用這個(gè)瀏覽器), npm run build即可.
(二) 安裝Karma-chrome-launch接下來(lái)安裝karma-chrome-launcher, 在命令行中輸入
npm install karma-chrome-launcher --save-dev
然后在項(xiàng)目中找到test/unit/karma.conf.js文件, 將PhantomJS瀏覽器修改為Chrome不要問(wèn)我為什么不使用PhantomJS, 因?yàn)榻?jīng)常莫名的錯(cuò)誤, 改成Chrome就不會(huì)!!!)
//karma.conf.js var webpackConfig = require("../../build/webpack.test.conf") module.exports = function (config) { config.set({ //browsers: ["PhantomJS"], browsers: ["Chrome"], ... }) }(三) 安裝Vue-test-utils
安裝Vue.js 官方的單元測(cè)試實(shí)用工具庫(kù), 在命令行輸入:
npm install --save-dev vue-test-utils(四) 執(zhí)行npm run unit
當(dāng)你完成以上兩步的時(shí)候, 你就可以在命令行執(zhí)行npm run unit嘗鮮你的第一次單元測(cè)試了, Vue腳手架已經(jīng)初始化了一個(gè)HelloWorld.spec.js的測(cè)試文件去測(cè)試HelloWrold.vue, 你可以在test/unit/specs/HelloWorld.spec.js下找到這個(gè)測(cè)試文件.(提示: 將來(lái)所有的測(cè)試文件, 都將放specs這個(gè)目錄下, 并以測(cè)試腳本名.spec.js結(jié)尾命名!)
在命令行輸入npm run unit, 當(dāng)你看到下圖所示的一篇綠的時(shí)候, 說(shuō)明你的單元測(cè)試通過(guò)了!
二. 測(cè)試工具的使用方法下面是一個(gè)Counter.vue文件, 我將以該文件為基礎(chǔ)講解項(xiàng)目中測(cè)試工具的使用方法.
//Counter.vue(一) Mocha框架 1. Mocha測(cè)試腳本的寫(xiě)法Counter.vue
{{ count }}
Mocha的作用是運(yùn)行測(cè)試腳本, 要對(duì)上面Counter.vue進(jìn)行測(cè)試, 我們就要寫(xiě)測(cè)試腳本, 通常測(cè)試腳本應(yīng)該與Vue組件名相同, 后綴為spec.js. 比如, Counter.vue組件的測(cè)試腳本名字就應(yīng)該為Counter.spec.js
//Counter.spec.js import Vue from "vue" import Counter from "@/components/Counter" describe("Counter.vue", () => { it("點(diǎn)擊按鈕后, count的值應(yīng)該為1", () => { //獲取組件實(shí)例 const Constructor = Vue.extend(Counter); //掛載組件 const vm = new Constructor().$mount(); //獲取button const button = vm.$el.querySelector("button"); //新建點(diǎn)擊事件 const clickEvent = new window.Event("click"); //觸發(fā)點(diǎn)擊事件 button.dispatchEvent(clickEvent); //監(jiān)聽(tīng)點(diǎn)擊事件 vm._watcher.run(); // 斷言:count的值應(yīng)該是數(shù)字1 expect(Number(vm.$el.querySelector(".num").textContent)).to.equal(1); }) })
上面這段代碼就是一個(gè)測(cè)試腳本.測(cè)試腳本應(yīng)該包含一個(gè)或多個(gè)describe, 每個(gè)describe塊應(yīng)該包括一個(gè)或多個(gè)it塊
describe塊稱(chēng)為"測(cè)試套件"(test suite), 表示一組相關(guān)的測(cè)試. 它是一個(gè)函數(shù), 第一個(gè)參數(shù)是測(cè)試套件的名稱(chēng)(通常寫(xiě)測(cè)試組件的名稱(chēng), 這里即為Counter.js), 第二個(gè)參數(shù)是一個(gè)實(shí)際執(zhí)行的函數(shù).
it塊稱(chēng)為"測(cè)試用例"(test case), 表示一個(gè)多帶帶的測(cè)試, 是測(cè)試的最小單位. 它也是一個(gè)函數(shù), 第一個(gè)參數(shù)是測(cè)試用例的名稱(chēng)(通常描述你的斷言結(jié)果, 這里即為"點(diǎn)擊按鈕后, count的值應(yīng)該為1"), 第二個(gè)參數(shù)是一個(gè)實(shí)際執(zhí)行的函數(shù).
2. Mocha進(jìn)行異步測(cè)試我們?cè)?b>Counter.vue組件中添加一個(gè)按鈕, 并添加一個(gè)異步自增的方法為incrementByAsync, 該函數(shù)設(shè)置一個(gè)延時(shí)器, 1000ms后count自增1.
... ...
給測(cè)試腳本中新增一個(gè)測(cè)試用例, 也就是it()
it("count異步更新, count的值應(yīng)該為1", (done) => { ///獲取組件實(shí)例 const Constructor = Vue.extend(Counter); //掛載組件 const vm = new Constructor().$mount(); //獲取button const button = vm.$el.querySelectorAll("button")[1]; //新建點(diǎn)擊事件 const clickEvent = new window.Event("click"); //觸發(fā)點(diǎn)擊事件 button.dispatchEvent(clickEvent); //監(jiān)聽(tīng)點(diǎn)擊事件 vm._watcher.run(); //1s后進(jìn)行斷言 window.setTimeout(() => { // 斷言:count的值應(yīng)該是數(shù)字1 expect(Number(vm.$el.querySelector(".num").textContent)).to.equal(1); done(); }, 1000); })
Mocha中的異步測(cè)試, 需要給it()內(nèi)函數(shù)的參數(shù)中添加一個(gè)done, 并在異步執(zhí)行完后必須調(diào)用done(), 如果不調(diào)用done(), 那么Mocha會(huì)在2000ms后報(bào)錯(cuò)且本次單元測(cè)試測(cè)試失敗(mocha默認(rèn)的異步測(cè)試超時(shí)上線為2000ms), 錯(cuò)誤信息如下:
3. Mocha的測(cè)試鉤子如果大家對(duì)于vue的mounted(), created()鉤子能夠理解的話, 對(duì)Mocha的鉤子也很容易理解, Mocha在describe塊中提供了四個(gè)鉤子: before(), after(), beforeEach(), afterEach(). 它們會(huì)在以下時(shí)間執(zhí)行
describe("鉤子說(shuō)明", function() { before(function() { // 在本區(qū)塊的所有測(cè)試用例之前執(zhí)行 }); after(function() { // 在本區(qū)塊的所有測(cè)試用例之后執(zhí)行 }); beforeEach(function() { // 在本區(qū)塊的每個(gè)測(cè)試用例之前執(zhí)行 }); afterEach(function() { // 在本區(qū)塊的每個(gè)測(cè)試用例之后執(zhí)行 }); });
上述就是Mocha的基本使用介紹, 如果想了解Mocha的更多使用方法, 可以查看下面的文檔和一篇阮一峰的Mocha教程:
Mocha官方文檔 : https://mochajs.org/
Mocha官方文檔翻譯 : http://www.jianshu.com/p/9c78...
阮一峰 - 測(cè)試框架 Mocha 實(shí)例教程 : http://www.ruanyifeng.com/blo...
(二) Chai斷言庫(kù)上面的測(cè)試用例中, 以expect()方法開(kāi)頭的就是斷言.
expect(Number(vm.$el.querySelector(".num").textContent)).to.equal(1);
所謂斷言, 就是判斷源碼的實(shí)際執(zhí)行結(jié)果與預(yù)期結(jié)果是否一致, 如果不一致, 就會(huì)拋出錯(cuò)誤. 上面的斷言的意思是指: 有.num這類(lèi)名的節(jié)點(diǎn)的內(nèi)容應(yīng)該為數(shù)字1. 斷言庫(kù)庫(kù)有很多種, Mocha并不限制你需要使用哪一種斷言庫(kù), Vue的腳手架提供的斷言庫(kù)是sino-chai, 是一個(gè)基于Chai的斷言庫(kù), 并且我們指定使用的是它的expect斷言風(fēng)格.
expect斷言風(fēng)格的優(yōu)點(diǎn)很接近于自然語(yǔ)言, 下面是一些例子
// 相等或不相等 expect(1 + 1).to.be.equal(2); expect(1 + 1).to.be.not.equal(3); // 布爾值為true expect("hello").to.be.ok; expect(false).to.not.be.ok; // typeof expect("test").to.be.a("string"); expect({ foo: "bar" }).to.be.an("object"); expect(foo).to.be.an.instanceof(Foo); // include expect([1,2,3]).to.include(2); expect("foobar").to.contain("foo"); expect({ foo: "bar", hello: "universe" }).to.include.keys("foo"); // empty expect([]).to.be.empty; expect("").to.be.empty; expect({}).to.be.empty; // match expect("foobar").to.match(/^foo/);
每一個(gè)it()所包裹的測(cè)試用例都應(yīng)該有一句或多句斷言,上面只是介紹了一部分的斷言語(yǔ)法, 如果想要知道更多Chai的斷言語(yǔ)法, 請(qǐng)查看以下的官方文檔.
Chai官方文檔: http://chaijs.com/
Chai官方文檔翻譯: http://www.jianshu.com/p/f200...
(三) Vue-test-utils測(cè)試庫(kù) 1. 在測(cè)試腳本中引入vue-test-utils//Counter.spec.js import Vue from "vue" import Counter from "@/components/Counter" //引入vue-test-utils import {mount} from "vue-test-utils"2. 測(cè)試文本內(nèi)容
下面我將在Counter.spec.js測(cè)試腳本中對(duì)Counter.vue中的文本內(nèi)容進(jìn)行測(cè)試, 大家可以直觀的感受一下使用了Vue-test-utils后對(duì).vue單文件組件的測(cè)試變得多么簡(jiǎn)單.
未使用vue-test-utils的測(cè)試用例:
it("未使用Vue-test-utils: 正確渲染h3的文字為Counter.vue", () => { const Constructor = Vue.extend(Counter); const vm = new Constructor().$mount(); const H3 = vm.$el.querySelector("h3").textContent; expect(H3).to.equal("Counter.vue"); })
使用了vue-test-utils的測(cè)試用例:
it("使用Vue-test-Utils: 正確渲染h3的文字為Counter.vue", () => { const wrapper = mount(Counter); expect(wrapper.find("h3").text()).to.equal("Counter.vue"); })
從上面的代碼可以看出, vue-test-utils工具將該測(cè)試用例的代碼量減少了一半, 如果是更復(fù)雜的測(cè)試用例, 那么代碼量的減少將更為突出. 它可以讓我們更專(zhuān)注于去寫(xiě)文件的測(cè)試邏輯, 將獲取組件實(shí)例和掛載的繁瑣的操作交由vue-test-utils去完成.
3. vue-test-utils的常用APIfind(): 返回匹配選擇器的第一個(gè)DOM節(jié)點(diǎn)或Vue組件的wrapper, 可以使用任何有效的選擇器
text(): 返回wrapper的文本內(nèi)容
html(): 返回wrapper DOM的HTML字符串
it("find()/text()/html()方法", () => { const wrapper = mount(Counter); const h3 = wrapper.find("h3"); expect(h3.text()).to.equal("Counter.vue"); expect(h3.html()).to.equal("Counter.vue
"); })
trigger(): 在該 wrapper DOM 節(jié)點(diǎn)上觸發(fā)一個(gè)事件。
it("trigger()方法", () => { const wrapper = mount(Counter); const buttonOfSync = wrapper.find(".sync-button"); buttonOfSync.trigger("click"); buttonOfSync.trigger("click"); const count = Number(wrapper.find(".num").text()); expect(count).to.equal(2); })
setData(): 設(shè)置data的屬性并強(qiáng)制更新
it("setData()方法",() => { const wrapper = mount(Counter); wrapper.setData({foo: "bar"}); expect(wrapper.vm.foo).to.equal("bar"); })
上面介紹了幾個(gè)vue-test-utils提供的方法, 如果想深入學(xué)習(xí)vue-test-utils, 請(qǐng)閱讀下面的官方文檔:
三. 項(xiàng)目說(shuō)明vue-test-utils官方文檔: https://vue-test-utils.vuejs....
該項(xiàng)目模仿了一個(gè)簡(jiǎn)單的微博, 在代碼倉(cāng)庫(kù)下載后, 可直接通過(guò)npm run dev運(yùn)行.
(一) 項(xiàng)目效果圖 (二) 項(xiàng)目中的交互邏輯和需求在文本框中輸入內(nèi)容后點(diǎn)擊"發(fā)布"按鈕(1), 會(huì)新發(fā)布內(nèi)容到微博列表中, 且個(gè)人頭像等下的微博數(shù)量(6)會(huì)增加1個(gè)
當(dāng)文本框中無(wú)內(nèi)容時(shí), 不能發(fā)布空微博到微博列表, 且彈出提示框, 叫用戶(hù)輸入內(nèi)容
當(dāng)點(diǎn)擊"關(guān)注"(2), 個(gè)人頭像下關(guān)注的數(shù)量(5)會(huì)增加1個(gè), 且按鈕內(nèi)字體變成"取消關(guān)注"; 當(dāng)點(diǎn)擊"取消關(guān)注"(2), 個(gè)人頭像下的數(shù)量(5)會(huì)減少1個(gè), 且按鈕內(nèi)字體變成"關(guān)注"
當(dāng)點(diǎn)擊"收藏"(3)時(shí), 我的收藏(7)會(huì)增加1個(gè)數(shù)量, 且按鈕內(nèi)文字變成"已收藏"; 點(diǎn)擊"已收藏"(3)時(shí), 我的收藏(7)會(huì)減少1個(gè)數(shù)量, 且按鈕內(nèi)文字變成"收藏"
當(dāng)點(diǎn)擊"贊"(4), 我的贊(8)會(huì)增加1個(gè)數(shù)量, 且按鈕內(nèi)文字變成"取消贊"; 點(diǎn)擊"取消贊"(3)時(shí), 我的贊(8)會(huì)減少1個(gè)數(shù)量, 且按鈕內(nèi)文字變成"贊"
(三) 項(xiàng)目源碼//SinaWeibo.vue四. 項(xiàng)目單元測(cè)試腳本實(shí)戰(zhàn) {{news.name}}{{news.resource}}{{news.content}}
- {{news.collect ? "已收藏" : "收藏"}}
- 轉(zhuǎn)發(fā)
- 評(píng)論
- {{news.like ? "取消贊" : "贊"}}
我們將以上文提到的"項(xiàng)目中的交互邏輯和需求"為基礎(chǔ), 為SinaWeibo.vue編寫(xiě)測(cè)試腳本, 下面我將展示測(cè)試用例編寫(xiě)過(guò)程:
1.在文本框中輸入內(nèi)容后點(diǎn)擊"發(fā)布"按鈕(1), 會(huì)新發(fā)布內(nèi)容到微博列表中, 且個(gè)人頭像等下的微博數(shù)量(6)會(huì)增加1個(gè)
it("點(diǎn)擊發(fā)布按鈕,發(fā)布新內(nèi)容&個(gè)人微博數(shù)量增加1個(gè)", () => { const wrapper = mount(SinaWeibo); const textArea = wrapper.find(".weibo-publish-wrapper textarea"); const buttonOfPublish = wrapper.find(".weibo-publish-wrapper button"); const lengthOfWeiboNews = wrapper.vm.weiboNews.length; const countOfMyWeibo = wrapper.vm.profileData[2].num; //設(shè)置textArea的綁定數(shù)據(jù) wrapper.setData({newWeiboContent: { imgUrl: "../../static/image/profile.jpg", name: "Lee_tanghui", resource: "剛剛 來(lái)自 網(wǎng)頁(yè)版微博", content: "歡迎來(lái)到我的微博", images: [] }}); //觸發(fā)點(diǎn)擊事件 buttonOfPublish.trigger("click"); const lengthOfWeiboNewsAfterPublish = wrapper.vm.weiboNews.length; const countOfMyWeiboAfterPublish = wrapper.vm.profileData[2].num; //斷言: 發(fā)布新內(nèi)容 expect(lengthOfWeiboNewsAfterPublish).to.equal(lengthOfWeiboNews + 1); //斷言: 個(gè)人微博數(shù)量增加1個(gè) expect(countOfMyWeiboAfterPublish).to.equal(countOfMyWeibo + 1); })
測(cè)試結(jié)果:
2.當(dāng)文本框中無(wú)內(nèi)容時(shí), 不能發(fā)布空微博到微博列表, 且彈出提示框, 叫用戶(hù)輸入內(nèi)容
it("當(dāng)文本框中無(wú)內(nèi)容時(shí), 不能發(fā)布空微博到微博列表, 且彈出提示框", () => { const wrapper = mount(SinaWeibo); const textArea = wrapper.find(".weibo-publish-wrapper textarea"); const buttonOfPublish = wrapper.find(".weibo-publish-wrapper button"); const lengthOfWeiboNews = wrapper.vm.weiboNews.length; const countOfMyWeibo = wrapper.vm.profileData[2].num; //設(shè)置textArea的綁定數(shù)據(jù)為空 wrapper.setData({newWeiboContent: { imgUrl: "../../static/image/profile.jpg", name: "Lee_tanghui", resource: "剛剛 來(lái)自 網(wǎng)頁(yè)版微博", content: "", images: [] }}); //觸發(fā)點(diǎn)擊事件 buttonOfPublish.trigger("click"); const lengthOfWeiboNewsAfterPublish = wrapper.vm.weiboNews.length; const countOfMyWeiboAfterPublish = wrapper.vm.profileData[2].num; //斷言: 沒(méi)有發(fā)布新內(nèi)容 expect(lengthOfWeiboNewsAfterPublish).to.equal(lengthOfWeiboNews); //斷言: 個(gè)人微博數(shù)量不變 expect(countOfMyWeiboAfterPublish).to.equal(countOfMyWeibo); })
測(cè)試結(jié)果:
3.當(dāng)點(diǎn)擊"關(guān)注"(2), 個(gè)人頭像下關(guān)注的數(shù)量(5)會(huì)增加1個(gè), 且按鈕內(nèi)字體變成"取消關(guān)注"; 當(dāng)點(diǎn)擊"取消關(guān)注"(2), 個(gè)人頭像下的數(shù)量(5)會(huì)減少1個(gè), 且按鈕內(nèi)字體變成"關(guān)注"
it("當(dāng)點(diǎn)擊"關(guān)注", 個(gè)人頭像下關(guān)注的數(shù)量會(huì)增加1個(gè), 且按鈕內(nèi)字體變成"取消關(guān)注"", () => { const wrapper = mount(SinaWeibo); const buttonOfAddAttendion = wrapper.find(".add"); const countOfMyAttention = wrapper.vm.profileData[0].num; //觸發(fā)事件 buttonOfAddAttendion.trigger("click"); const countOfMyAttentionAfterClick = wrapper.vm.profileData[0].num; //斷言: 個(gè)人頭像下關(guān)注的數(shù)量會(huì)增加1個(gè) expect(countOfMyAttentionAfterClick).to.equal(countOfMyAttention + 1); //斷言: 按鈕內(nèi)字體變成"取消關(guān)注 expect(buttonOfAddAttendion.text()).to.equal("取消關(guān)注"); })
it("當(dāng)點(diǎn)擊"取消關(guān)注", 個(gè)人頭像下關(guān)注的數(shù)量會(huì)減少1個(gè), 且按鈕內(nèi)字體變成"關(guān)注"", () => { const wrapper = mount(SinaWeibo); const buttonOfUnAttendion = wrapper.find(".cancel"); const countOfMyAttention = wrapper.vm.profileData[0].num; //觸發(fā)事件 buttonOfUnAttendion.trigger("click"); const countOfMyAttentionAfterClick = wrapper.vm.profileData[0].num; //斷言: 個(gè)人頭像下關(guān)注的數(shù)量會(huì)增加1個(gè) expect(countOfMyAttentionAfterClick).to.equal(countOfMyAttention - 1); //斷言: 按鈕內(nèi)字體變成"取消關(guān)注 expect(buttonOfUnAttendion.text()).to.equal("關(guān)注"); })
測(cè)試結(jié)果:
4.當(dāng)點(diǎn)擊"收藏"(3)時(shí), 我的收藏(7)會(huì)增加1個(gè)數(shù)量, 且按鈕內(nèi)文字變成"已收藏"; 點(diǎn)擊"已收藏"(3)時(shí), 我的收藏(7)會(huì)減少1個(gè)數(shù)量, 且按鈕內(nèi)文字變成"收藏"
it("當(dāng)點(diǎn)擊"收藏"時(shí), 我的收藏會(huì)增加1個(gè)數(shù)量, 且按鈕內(nèi)文字變成"已收藏"", () => { const wrapper = mount(SinaWeibo); const buttonOfCollect = wrapper.find(".collectWeibo"); const countOfMyCollect = Number(wrapper.find(".collect-num span").text()); //觸發(fā)點(diǎn)擊事件 buttonOfCollect.trigger("click"); const countOfMyCollectAfterClick = Number(wrapper.find(".collect-num span").text()); //斷言: 我的收藏?cái)?shù)量會(huì)加1 expect(countOfMyCollectAfterClick).to.equal(countOfMyCollect + 1); //斷言: 按鈕內(nèi)文字變成已收藏 expect(buttonOfCollect.text()).to.equal("已收藏"); })
it("當(dāng)點(diǎn)擊"已收藏"時(shí), 我的收藏會(huì)減少1個(gè)數(shù)量, 且按鈕內(nèi)文字變成"收藏"", () => { const wrapper = mount(SinaWeibo); const buttonOfUnCollect = wrapper.find(".uncollectWeibo"); const countOfMyCollect = Number(wrapper.find(".collect-num span").text()); //觸發(fā)點(diǎn)擊事件 buttonOfUnCollect.trigger("click"); const countOfMyCollectAfterClick = Number(wrapper.find(".collect-num span").text()); //斷言: 我的收藏?cái)?shù)量會(huì)減1 expect(countOfMyCollectAfterClick).to.equal(countOfMyCollect - 1 ); //斷言: 按鈕內(nèi)文字變成已收藏 expect(buttonOfUnCollect.text()).to.equal("收藏"); })
測(cè)試結(jié)果:
5.當(dāng)點(diǎn)擊"贊"(4), 我的贊(8)會(huì)增加1個(gè)數(shù)量, 且按鈕內(nèi)文字變成"取消贊"; 點(diǎn)擊"取消贊"(3)時(shí), 我的贊(8)會(huì)減少1個(gè)數(shù)量, 且按鈕內(nèi)文字變成"贊"
it("當(dāng)點(diǎn)擊"贊", 我的贊會(huì)增加1個(gè)數(shù)量, 且按鈕內(nèi)文字變成"取消贊"", () => { const wrapper = mount(SinaWeibo); const buttonOfLike = wrapper.find(".dislikedWeibo"); const countOfMyLike = Number(wrapper.find(".like-num span").text()); //觸發(fā)點(diǎn)擊事件 buttonOfLike.trigger("click"); const countOfMyLikeAfterClick = Number(wrapper.find(".like-num span").text()); //斷言: 我的贊會(huì)增加1個(gè)數(shù)量 expect(countOfMyLikeAfterClick).to.equal(countOfMyLike + 1); //斷言: 按鈕內(nèi)文字變成取消贊 expect(buttonOfLike.text()).to.equal("取消贊"); });
it("當(dāng)點(diǎn)擊"取消贊", 我的贊會(huì)減少1個(gè)數(shù)量, 且按鈕內(nèi)文字變成"贊"", () => { const wrapper = mount(SinaWeibo); const buttonOfDislike = wrapper.find(".likedWeibo"); const countOfMyLike = Number(wrapper.find(".like-num span").text()); //觸發(fā)點(diǎn)擊事件 buttonOfDislike.trigger("click"); const countOfMyLikeAfterClick = Number(wrapper.find(".like-num span").text()); //斷言: 我的贊會(huì)增加1個(gè)數(shù)量 expect(countOfMyLikeAfterClick).to.equal(countOfMyLike - 1); //斷言: 按鈕內(nèi)文字變成取消贊 expect(buttonOfDislike.text()).to.equal("贊"); });
測(cè)試結(jié)果
項(xiàng)目地址:Git倉(cāng)庫(kù): https://github.com/Lee-Tanghu...參考文章
測(cè)試框架 Mocha 實(shí)例教程 - 阮一峰
Chai.js斷言庫(kù)API中文文檔
知乎: 如果對(duì)vue進(jìn)行單元測(cè)試
Vue.js學(xué)習(xí)系列六——Vue單元測(cè)試Karma+Mocha學(xué)習(xí)筆記
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/90554.html
摘要:但是,項(xiàng)目中的一些公共封裝,比如公共的組件公用的功能模塊等是可以使用單元測(cè)試的。因此特為組件庫(kù)引入單元測(cè)試,目的在于能減少組件的,避免重復(fù)的發(fā)布不必要的包。 項(xiàng)目github地址:https://github.com/yuanalina/installAsRequired這里必須要提前說(shuō)明,前端項(xiàng)目的單元測(cè)試不是必須的,特別是業(yè)務(wù)型項(xiàng)目,增加單元測(cè)試反而會(huì)成為累贅,增加開(kāi)發(fā)成本且無(wú)意義...
摘要:為保證代碼的質(zhì)量,單元測(cè)試必不可少。本文記錄自己在學(xué)習(xí)單元測(cè)試過(guò)程中的一些總結(jié)。以一個(gè)項(xiàng)目為例,代碼結(jié)構(gòu)如下前端測(cè)試框架主要是與,這里我們選擇,斷言庫(kù)有以及自帶的。 為保證代碼的質(zhì)量,單元測(cè)試必不可少。本文記錄自己在學(xué)習(xí)單元測(cè)試過(guò)程中的一些總結(jié)。 TDD與BDD的區(qū)別 TDD屬于測(cè)試驅(qū)動(dòng)開(kāi)發(fā),BDD屬于行為驅(qū)動(dòng)開(kāi)發(fā)。個(gè)人理解其實(shí)就是TDD先寫(xiě)測(cè)試模塊,再寫(xiě)主功能代碼,然后能讓測(cè)試模塊通...
摘要:基礎(chǔ)知識(shí)作用為提供瀏覽器測(cè)試環(huán)境,為真正測(cè)試框架,為斷言庫(kù)測(cè)試用例基礎(chǔ)塊稱(chēng)為測(cè)試套件,表示一組相關(guān)的測(cè)試。它也是一個(gè)函數(shù),第一個(gè)參數(shù)是測(cè)試用例的名稱(chēng),第二個(gè)參數(shù)是一個(gè)實(shí)際執(zhí)行的函數(shù)。 基礎(chǔ)知識(shí) karma作用為提供瀏覽器測(cè)試環(huán)境,mocha為真正測(cè)試框架,chai為斷言庫(kù) 測(cè)試用例基礎(chǔ) describe塊稱(chēng)為測(cè)試套件(test suite),表示一組相關(guān)的測(cè)試。它是一個(gè)函數(shù),第一個(gè)...
摘要:的配置文件是為了解析那些需要測(cè)試的源文件相關(guān)的文件,然后再給的單元測(cè)試用例去識(shí)別。其作用是僅僅渲染至虛擬節(jié)點(diǎn),不會(huì)返回真實(shí)的節(jié)點(diǎn),能極大提高測(cè)試性能。 為解放勞動(dòng)力,發(fā)展生產(chǎn)力 測(cè)試有了這般變化: 鼠標(biāo)點(diǎn)擊手動(dòng)測(cè)試 -> 用腳本模擬,自動(dòng)化測(cè)試 Vue中的組件測(cè)試 需要安裝的包 全局安裝:babel、mocha、karma 其他局部安裝的包在下面的【測(cè)試環(huán)境搭建】最下方配置文件中給出...
摘要:安裝安裝依賴(lài)庫(kù)安裝已經(jīng)相關(guān)的插件,可以使用或者使用在這篇文章中,我使用和,如果你不喜歡這兩個(gè)庫(kù),你可以選擇你喜歡的任何一個(gè)庫(kù),只要它能在瀏覽器中運(yùn)行就可以。 本文翻譯自:Automated testing with Headless Chrome作者:Eric Bidelman (Google 工程師)譯者:justjavac 如果您想使用 Headless Chrome 進(jìn)行自動(dòng)測(cè)試...
閱讀 3272·2021-11-15 11:37
閱讀 1085·2021-11-02 14:45
閱讀 3905·2021-09-04 16:48
閱讀 3582·2019-08-30 15:55
閱讀 757·2019-08-23 17:53
閱讀 1000·2019-08-23 17:03
閱讀 2032·2019-08-23 16:43
閱讀 2191·2019-08-23 16:22