大廠視野
1.流量大
- 性能優化
2.項目復雜
- 源碼
3.持續迭代
4.項目穩定運行
- 自動化測試
- 需求評審
- 開發
- 測試(人點)(可以使用自動化測試完成一部分)
- 上線
5.為什么要寫測試呢
- 測試代碼可讀性好,易於維護
- 替代了測試的部分工作,無形中完成了一部分任務
准備
1.了解自動化測試
2.jest
3.cypress
4.端對端和自動化
測試分類
常⻅見的開發流程⾥里里,都有測試⼈人員,這種我們成為⿊黑盒測試,
測試⼈人員不不管內部實現機制,只看最外層的輸⼊入輸出,⽐比如你寫⼀一個加法的⻚頁⾯面,會設計 N個case,測試加法的正確性,這種代碼⾥里里,我們稱之為E2E測試
更更負責⼀一些的,我們稱之為集成測試,就是集合多個測試過的單元⼀一起測試
還有⼀一種測試叫做⽩白盒測試,我們針對⼀一些內部機制的核⼼心邏輯 使⽤用代碼 進⾏行行編寫 我們稱之為單元測試
這仨都是我們前端開發⼈人員進階必備的技能!
我們其實⽇日常使⽤用 console,算是測試的雛形吧,console.log(add(1,2) == 3)
測試的好處
組件的單元測試有很多好處:
- 提供描述組件⾏行行為的⽂文檔
- 節省⼿手動測試的時間
- 減少研發新特性時產⽣生的 bug
- 改進設計
- 促進重構
⾃自動化測試使得⼤大團隊中的開發者可以維護復雜的基礎代碼。讓你改代碼不不再⼩小⼼心翼翼
我們先來創建一個vue項目
后面選擇jest和cypress
裝好了以后有個test目錄,下有兩個目錄,unit和e2e,unit是單測,e2e是端對端
npm run test:unit 就可以進行測試了
單元測試
單元測試(unit testing),是指對軟件中的最⼩小可測試單元進⾏行行檢查和驗證。例如一個函數。
單測針對組件 或者函數 或者模塊(開發人員知道具體邏輯)
在vue中,推薦⽤用 Mocha+chai 或者jest,咱們使⽤用 jest演示,語法基本⼀一致
😊寫一個demo
在src建立utils.js,寫個add函數導出
export function add(x,y) {
return x + y
}
在tests下的unit下的example.spec.js中修改(當然也可以新建一個*.spec.js,固定格式的文件)
// import { shallowMount } from '@vue/test-utils'
// import HelloWorld from '@/components/HelloWorld.vue'
// describe('HelloWorld.vue', () => {
// it('renders props.msg when passed', () => {
// const msg = 'new message'
// const wrapper = shallowMount(HelloWorld, {
// propsData: { msg }
// })
// expect(wrapper.text()).toMatch(msg)
// })
// })
import { add } from '@/utils.js'
describe('測試加法函數',()=>{
//測試代碼可讀性最好
//分組
it('一個具體的功能測試,測測試數字相加',()=>{
expect( add(1,2) ).toBe(3)
})
it('一個具體的功能測試,測測試數字和字符串相加',()=>{
expect( add('a',2) ).toBe('a2')
})
it('一個具體的功能測試,測測試數字字符串相加',()=>{
expect( add('1',2) ).toBe(3)
})
})
npm run test:unit 后
測試結果為失敗(不通過)
這個案例我們就用到了4個api
api介紹
- describe : 定義⼀一個測試套件
- it :定義⼀一個測試⽤用例例
- expect :斷⾔言的判斷條件
- toBe :斷⾔言的⽐比較結果
測試vue組件
components下新建KaiKeBa.vue
<template>
<div>
<span>{{ msg }}</span>
<span>{{ msg1 }}</span>
<button class="btn" @click="changeMsg">點我</button>
</div>
</template>
<script>
export default {
data(){
return {
msg: "vue test",
msg1: '你好'
}
},
created() {
this.msg = 'aftermounted'
},
mounted() {
this.msg1 = '開課吧'
},
methods: {
changeMsg(){
this.msg = 'click over'
}
},
}
</script>
views目錄下的home組件中引入
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<KaiKeBa></KaiKeBa>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
import KaiKeBa from '@/components/KaiKeBa.vue'
export default {
name: 'home',
components: {
KaiKeBa,
HelloWorld
}
}
</script>
在tests目錄下的unit目錄下建立kaikeba.spec.js
import Vue from 'vue'
import KaiKeBaComp from '@/components/KaiKeBa.vue'
import { mount } from '@vue/test-utils'
describe('測試KaiKeBa組件',() => {
it('測試初始化的data',() => {
expect( KaiKeBaComp.data().msg ).toBe('vue test')
})
//created和mounted里數據測試都是一樣的
it('測試新建完畢后,create生命周期后的數據',() => {//created
let vm = new Vue(KaiKeBaComp).$mount()
expect(vm.msg).toBe('aftermounted')
})
it('測試新建完畢后,create生命周期后的數據',() => {//mounted
let vm = new Vue(KaiKeBaComp).$mount()
expect(vm.msg1).toBe('開課吧')
})
//點擊事件測試
it('測試點擊后,msg的改變',() => {
// $mount處理不了用戶交互,所以我們要用到vue官方推薦的@vue/test-utils
let wrapper = mount(KaiKeBaComp)
expect( wrapper.vm.msg ).toBe('aftermounted')
//點擊一下
wrapper.find('.btn').trigger('click')
expect( wrapper.vm.msg ).toBe('click over')
})
})
如果測試用戶交互的話,需要用到官方推薦的@vue/test-utils,執行cnpm i @vue/test-utils --save,相關文檔在vue官網
在 @vue/test-utils 中引入mount替代vue的$mount的是因為$mount的是虛擬的,存在虛擬內存中,處理不了dom,所以用mount(對不對我不知道,這句話僅供參考)
測試覆蓋率
jest⾃自帶覆蓋率,如果⽤用的 mocha,需要使⽤用 istanbul來統計覆蓋率
package.json⾥里里修改 jest配置
jest: {
"collectCoverage": true,
"collectCoverageFrom": ["src/**/*.{js,vue}"],
}
倘若有個jest.config.js那么就在 moduleFileExtensions 上一行加入,這個文件是對jest的配置
"collectCoverage": true,
"collectCoverageFrom": ["src/**/*.{js,vue}"],
在跑一下npm run test:unit
生成的報告文件在coverage目錄下,打開可以看到這么個鬼東西
我們的測試報告
這個測試報告可以很精確的看到我們哪些沒測試,哪些測試了,我在KaiKeBa里加點東西不測試,來看看結果
藍色的我們可以點進去的
指出了沒有測到的地方,判斷了newData是否等於1,顯然不等於的
我們可以給他測試下,要這么寫,給newData賦值
it('測試點擊后,newData == "1"的結果',() => {
// $mount處理不了用戶交互,所以我們要用到vue官方推薦的@vue/test-utils
let wrapper = mount(KaiKeBaComp)
wrapper.vm.newData = '1'
//點擊一下
wrapper.find('.btn').trigger('click')
expect( wrapper.vm.msg ).toBe('click false')
})
如何測試不通過就阻止代碼git提交
安裝husky:cnpm i husky --save
配置husky,在package.json
"husky": {
"hooks": {
"pre-commit": "npm test",
"pre-push": "npm test",
"...": "..."
}
}
當然這是文檔的,我們要按我們所需的改成
"husky": {
"hooks": {
"pre-commit": "npm run test:unit"
}
}
代表再提交(commit)前先執行npm run test:unit
🐱🚀驗證:
修改測試代碼文件kaikeba.spec.js
由
it('測試新建完畢后,create生命周期后的數據',() => {//created
let vm = new Vue(KaiKeBaComp).$mount()
expect(vm.msg).toBe('aftermounted')
})
改為
it('測試新建完畢后,create生命周期后的數據',() => {//created
let vm = new Vue(KaiKeBaComp).$mount()
expect(vm.msg).toBe('aftermounted1')
})
這樣測試肯定是不通過的
這時,我們來提交代碼,add后再commit
如圖:提交被終止
那么我們再將測試文件改回來,讓測試可以通過
如圖:測試通過,代碼也commit提交了
E2E測試
e2e針對應用,站在測試人員的角度,沒有什么mount 加載,只有按鈕 頁面,輸入框,文本等
借⽤瀏覽器器的能力,站在⽤戶測試⼈員的角度,輸⼊框,點擊按鈕等,完全模擬用戶,這個和具體的框架關系不大,完全模擬瀏覽器行為
😒將views下的Home組件的
<HelloWorld msg="Welcome to Your Vue.js App"></HelloWorld>
代碼恢復
😒看看tests目錄下e2e目錄下的spec下的test.js,有這么一段
// https://docs.cypress.io/api/introduction/api.html
describe('My First Test', () => {
it('Visits the app root url', () => {
cy.visit('/')
cy.contains('h1', 'Welcome to Your Vue.js App')
})
})
這是測試代碼,如果我們不做前面的恢復操作的話,測試肯定是不通過的
😒執行npm run test:e2e,進行測試
項目會啟動,並且會彈出一個有ok的彈窗,我們直接確定,還會有測試文件的js,選擇對應的測試文件點進去
這樣就已經測試通過了
😒再來測試一個about頁
// https://docs.cypress.io/api/introduction/api.html
describe('My First Test', () => {
it('Visits the app root url', () => {
//訪問根目錄
cy.visit('/')
cy.contains('h1', 'Welcome to Your Vue.js App')
})
it('測試about頁', () => {
//訪問about
cy.visit('about')
cy.contains('h1', 'This is an about page')
})
})
結果是這個樣子
后測的about,就最后打開了about頁,也停留在了about頁面
這些都是頁面某個元素的文本的測試,那么我們再來個交互試試,在我們的KaiKeBa組件上有個點擊事件,我們來試試
// https://docs.cypress.io/api/introduction/api.html
describe('My First Test', () => {
it('Visits the app root url', () => {
//訪問根目錄
cy.visit('/')
cy.contains('h1', 'Welcome to Your Vue.js App')
})
it('測試about頁', () => {
//訪問about
cy.visit('about')
cy.contains('h1', 'This is an about page')
})
it('KaiKeBa組件',() => {
//訪問根目錄
cy.visit('/')
cy.contains('#msg','aftermounted')
cy.get('button').click()//點擊button元素
cy.contains('#msg','click over')
})
})
左邊那一條條可以點擊,點擊不同的項,也會進入不同的事件狀態,例如我點了CONTAINS,文本變成了click over
題外話:測試頁面前后差異,或者兩個頁面的差異可以用page-monitor;地址: https://github.com/fouber/page-monitor