1. 程式人生 > 實用技巧 >如何為我的VUE專案編寫高效的單元測試--Jest

如何為我的VUE專案編寫高效的單元測試--Jest

Unit Testing(單元測試)--Jest

一個完整的測試程式通常由幾種不同的測試組合而成,比如end to end(E2E)測試,有時還包括整體測試、簡要測試和單元測試。這裡要介紹的是Vue中的單元測試,使用流行的Jest JavaScript測試庫來執行我們的測試。

1.測試的目的

  • 排除故障

    每個應用的開發中,多少會出現一些意料之外的bug。通過測試應用程式,可以幫助我們大大減少此類問題,並增強應用程式的邏輯性。

  • 保證團隊成員的邏輯統一

    如果您是團隊的新成員,並且對應用程式還不熟悉,那麼一組測試就好像是有經驗的開發人員監視你編寫程式碼,確保您處於程式碼應該執行的正確路線之內。通過這些測試,您可以確信在新增新功能或更改現有程式碼時不會破壞任何東西。

  • 可以提高質量程式碼

    當您在編寫vue元件時,由於考慮到測試,最好的方案將是建立獨立的、更可重用的元件。如果您開始為您的元件編寫測試,並且您注意到這些元件不容易測試,那麼您可能會重構您的元件,最終起到改進它們的效果。

  • 起到很好的說明文件作用

    正如在第一點中提到的,測試的另一個結果是,它最終可以為您的開發團隊生成良好的文件。當某人對程式碼庫還不熟悉時,他們可以檢視測試以獲得指導,這可以提供關於元件應該如何工作的意圖的洞察,併為可能要測試的邊緣部分提供線索。

1.我們需要測試什麼

在測試的過程中,很容易照成過分的測試一些不需要的東西,這會不必要地減慢開發時間。那麼我們在Vue.js應用程式中測試什麼呢?答案其實很簡單:元件。由於Vue應用程式是一個個元件相互組合而成的,我們需要測試它們各自的行為,以確保它們正常工作。

元件原理及分解

我們可以先分解元件,看看元件之間是怎麼協調工作的。

一般來說,元件需要遵照我們定義的邏輯來工作,這裡有一個輸入和輸出的概念。元件會接收一些靜態或動態的輸入值,然後依照邏輯輸出一些值或者是dom元素。

正常來說,元件的輸入輸出有以下這些:

 1//輸入值
2
3//靜態接收資料
4ComponentData
5//動態接收資料
6ComponentProps
7//使用者互交,例如:一個使用者單擊的按鈕
8UserInteraction
9//生命週期邏輯,例如:mounted(),created()
10LifecycleMethods
11//元件狀態值
12VuexStore
13//路由引數
14RouteParams
15

16//輸出值
17
18//輸出的dom
19WhatisrenderedtotheDOM
20//外部呼叫的函式
21Externalfunctioncalls
22//元件觸發的事件
23Eventsemittedbythecomponent
24//路由的變更
25RouteChanges
26//元件狀態值得更新
27UpdatestotheVuexStore
28//對子元件的改變
29Connectionwithchildren
30

我們可以識別元件的輸入和輸出,挑選我們應該測試的內容,從而避開測試元件的內部業務邏輯。換句話說,我們不應該因為擔心每一行程式碼如何工作而陷入困境。

這看起來可能有悖常理,但是單元測試的目標純粹是確保元件產生預期的結果。我們在這裡不關心它是如何得出這個結果的。後期我們甚至可能會改變我們的邏輯方式,所以我們不希望測試如何實現這些邏輯。這些邏輯不是測試的工作。就單元測試而言,我們只要確保元件的輸出正常就行了。

要測試的部分

 1<template>
2<div>
3<buttonv-show="loggedIn">Logout</button>
4</div>
5</template>
6
7<script>
8exportdefault{
9data(){
10return{
11loggedIn:false
12}
13}
14}
15
</script>

在這個例子中,我們有一個元件,如果loggedIn屬性為true,它將顯示一個logout按鈕。為了弄清楚我們應該測試這個元件的哪個部分,我們的第一步是確定元件的輸入和輸出。

 1input
2
3//靜態資料
4data=>loggedIn
5//這個資料屬性決定按鈕是否顯示,所以這是一個我們應該測試的輸入
6
7output
8
9Dom輸出(button)
10根據輸入(loggedIn),我們的按鈕應該在什麼時候顯示在DOM中

對於更復雜的元件,將有更多的方面需要測試,但同樣的方法也適用。

不測試的部分

對你應該測試的東西知道大概當然是必要的,知道你不應該測試的東西也是有幫助的。如下:

  • 不測試元件邏輯的詳情(implementation details)

    當單元測試時,我們不需要為某些邏輯是如何工作而煩惱,只要它們確實工作就行了。我們不在乎這裡的內部結構。我們只關心元件產生了我們期望的輸出。

  • 不要測試框架本身

    開發人員經常試圖測試太多,包括框架本身的內部工作。但是框架作者已經建立了這樣的測試。

    例如,props中的資料型別,如果我們嘗試傳入錯誤資料,Vue將丟擲錯誤。我們不需要浪費時間測試Vue.js框架。這包括不要對Vue路由器和Vuex進行不必要的測試。

  • 不測試第三方庫

    如果您使用的第三方庫是高質量的,那麼它們已經有了自己的測試。我們不需要測試它們的內部結構。例如,我們不需要測試Axios是如何工作的。Axios隊為我們做了這件事。

2.編寫單元測試

當我們使用Vue CLI建立應用專案時:

  1. 首先需要選擇Manually select features自定義一些專案配置
  2. 然後在選擇專案依賴時,勾選Unit Testing選項
  3. 最後在選擇測試方案時。選擇Jest
  • vue/cli專案可以這樣安裝
1vueadd@vue/unit-jest
2
3✔Successfullyinstalledplugin:@vue/cli-plugin-unit-jest
4//如果安裝的jest執行報錯可能是版本問題,可以嘗試npmupdate

這樣建立的專案中就為我們安裝好了依賴。

開啟專案後,讓我們從檢視package.json開始,在這裡我們將看到為我們安裝了Jest和vue測試實用程式。

1//package.json
2"devDependencies":{
3"@vue/cli-plugin-unit-jest":"^3.11.0",
4"@vue/test-utils":"1.0.0-beta.29"
5}

Jest是一個JavaScript測試框架,它專注於簡化單元測試。Jest將為我們執行單元測試,並在測試通過或失敗時向我們報告。

API list:

 1afterAll(fn,timeout)
2afterEach(fn,timeout)
3beforeAll(fn,timeout)
4beforeEach(fn,timeout)
5describe(name,fn)
6describe.each(table)(name,fn,timeout)
7describe.only(name,fn)
8describe.only.each(table)(name,fn)
9describe.skip(name,fn)
10describe.skip.each(table)(name,fn)
11test(name,fn,timeout)
12test.each(table)(name,fn,timeout)
13test.only(name,fn,timeout)
14test.only.each(table)(name,fn)
15test.skip(name,fn)
16test.skip.each(table)(name,fn)
17test.todo(name)

@vue/test-utils是Vue.js的官方單元測試實用程式庫。它使我們能夠在測試中渲染元件,然後對這些渲染的元件執行各種操作。這使得我們可以測試元件的執行結果。

如何執行Jest測試,我們來檢視package.json中的執行指令碼

1"scripts":{
2...
3"test:unit":"vue-cli-servicetest:unit"
4}
5//在我們編寫好測試程式碼後只要執行:
6npmruntest:unit
7//或者在CLI中執行測試就可以執行測試程式碼
8//這個命令的意思就是檢視tests/unit目錄,並執行目錄下名為[componentsName].spec.js的檔案。

檢視專案根目錄下的tests/unit資料夾,有一個初始化的Example.spec.js檔案。spec是specification的縮寫,即詳述元件執行規則的js檔案

  • ## demo1(測試一個方法)

建立函式檔案demo1.js

1functionsum(a,b){
2returna+b;
3}
4module.exports=sum;

在tests/unit/中建立demo1.spec.js

 1//匯入要測試的檔案
2constsum=require('./sum')
3
4//使用test(name,fn,timeout)建立一個測試
5test('adds1+2toequal3',()=>{
6expect(sum(1,2)).toBe(3)
7})
8
9//其中,name是對測試的描述,測試結果會列印
10//fn是要執行的測試
11//expect()方法中描述的是測試的結果
12//toBe()是測試預期的值

執行這個單元測試

1npmruntest:unit
2
3PASStests/unit/demo1.spec.js
4✓adds1+2toequal3(5ms)
5//PASS表示測試通過,函式執行正常
6//下面列出每一項text(),[test.name(測試使用的時間)]
  • ## demo2(測試一個元件)

建立元件demo2.vue

 1<template>
2<div>
3<buttonv-show="loggedIn">Logout</button>
4</div>
5</template>
6<script>
7exportdefault{
8data(){
9return{
10loggedIn:false
11}
12}
13}
14
</script>

關於這個元件,前面我們已經介紹過了,要測試的點如下:

  1. 如果使用者未登入,則不顯示“登出”按鈕
  2. 如果使用者已登入,則顯示“登出”按鈕

在tests/unit/中建立demo2.spec.js

 1//匯入要測試的元件
2importdemo2from'@/components/demo2'
3
4//因為我們要測試的是元件,所以我們在測試時需要載入這個元件
5//`@vue/test-utils`為我們提供了這項功能
6import{mount}from'@vue/test-utils'
7//使用describe(name,fn)建立一個測試組
8//當我們有多個測試時,用這種方式組織它們比較具有邏輯性
9
10describe('ComponentsDemo2',()=>{
11
12//其中寫所有元件內需要的測試
13test('ifuserisnotloggedin,donotshowlogoutbutton',()=>{
14//安裝元件
15constwrapper=mount(AppHeader)
16
17//預設登入狀態是false,則找到元件內的btn,檢視其可見性為false
18expect(wrapper.find('button').isVisible()).toBe(false)
19})
20test('ifauserisloggedin,showlogoutbutton',()=>{
21//安裝元件
22constwrapper=mount(AppHeader)
23
24//要測試登入狀態是‘已登入’,則先設定其狀態值
25wrapper.setData({loggedIn:true})
26
27//這時找到元件內的btn,檢視其可見性為true
28expect(wrapper.find('button').isVisible()).toBe(true)
29})
30})

提示:

您可能還會看到使用it()的測試塊,它是test()的別名。

@vue/test-utils,還提供shallowMount()方法。如果元件有子元件,shallowMount()將返回該元件的本身,而不是完全渲染的詳細元件單元測試的焦點是單一的元件,多數時候我們會忽略其子元件。

執行這個單元測試

 1npmruntest:unit
2
3PASStests/unit/demo2.spec.js
4ComponentsDemo2
5✓ifuserisnotloggedin,donotshowlogoutbutton(20ms)
6ifauserisloggedin,showlogoutbutton(30ms)
7
8TestSuites:1passed,1total
9Tests:2passed,2total
10Snapshots:0total
11Time:2.653s
12Ranalltestsuites.

步驟總結如下圖:

  1. 建立一個測試組:describe()
  2. 開始一個測試:test()
  3. 安裝渲染元件:mount()
  4. 必要的時候設定元件引數:setData()
  5. 除錯預期的元件行為及資料正確與否:expect()

長按二維碼關注公眾號