您當前的位置:首頁 > 繪畫

一篇文章學會 Vue 專案單元測試

作者:由 王力國 發表于 繪畫時間:2018-11-08

關注微信公眾號“依賴注入”以獲得更好閱讀體驗。

這篇文章有三部分,閱讀完大概需要 10 分鐘(程式碼塊較多,建議使用電腦瀏覽)

一: 搭建基於 jest 的 vue 單元測試環境

二: 使用 vue-test-util 提高測試編碼效率

三: 複雜場景下的測試(模組,非同步,rxjs)

第一部分: 搭建基於 jest 的 vue 單元測試環境

因為 jest 包含了 karma + mocha + chai + sinon 的所有常用功能,零配置開箱即用,所以這個教程只講解 jest。

1.安裝依賴

npm install jest vue-jest babel-jest @vue/test-utils -D

2.編寫 jest 配置檔案

// 。/test/unit/jest。conf。js

const

path

=

require

‘path’

);

module

exports

=

{

rootDir

path

resolve

__dirname

‘。。/。。/’

),

// 類似 webpack。context

moduleFileExtensions

// 類似 webpack。resolve。extensions

‘js’

‘json’

‘vue’

],

moduleNameMapper

{

‘^@/(。*)$’

/src/$1’

// 類似 webpack。resolve。alias

},

transform

{

// 類似 webpack。module。rules

‘^。+\\。js$’

/node_modules/babel-jest’

‘。*\\。(vue)$’

/node_modules/vue-jest’

},

setupFiles

/test/unit/setup’

],

// 類似 webpack。entry

coverageDirectory

/test/unit/coverage’

// 類似 webpack。output

collectCoverageFrom

// 類似 webpack 的 rule。include

‘src/**/*。{js,vue}’

‘!src/main。js’

‘!src/router/index。js’

‘!**/node_modules/**’

],

};

3.編寫啟動檔案 setup.js

// 。/test/unit/setup。js

import

Vue

from

‘vue’

Vue

config

productionTip

=

false

4.加入啟動 jest 的 npm script

“scripts”

{

“unit”

“jest ——config test/unit/jest。conf。js ——coverage”

}

5.編寫第一個測試檔案

有一個元件

// 。/src/components/hello-world/hello-world。vue

<

template

>

<

div

>

<

h1

>

{{

msg

}}

<

/h1>

<

/div>

<

/template>

<

script

>

export

default

{

name

‘HelloWorld’

data

()

{

return

{

msg

‘Hello Jest’

};

},

};

<

/script>

對該元件進行測試

// 。/src/components/hello-world/hello-world。spec。js

import

{

shallowMount

}

from

‘@vue/test-utils’

import

HelloWorld

from

‘。/hello-world’

describe

()

=>

{

it

‘should render correct contents’

()

=>

{

const

wrapper

=

shallowMount

HelloWorld

);

expect

wrapper

find

‘。hello h1’

)。

text

())

toEqual

‘Welcome to Your Vue。js App’

);

});

});

6.啟動測試

npm run unit

jest 會自動掃描專案目錄下所有檔名以 。spec。js/。test。js 結尾的測試檔案,並執行測試用例。

一篇文章學會 Vue 專案單元測試

最後最佳化一下測試編碼體驗

到上一步我們已經可以開始編寫測試程式碼了,但每次對程式碼的改動都需要手動執行 jest 啟動命令,沒有類似 hot-reload 的功能很難受。

可能你有一百種方式可以解決這個需求,但是我現在想告訴你一個最簡單且體驗最好的一種方式 -> 在 vscode 編輯器安裝一個名為

jest

的外掛

一篇文章學會 Vue 專案單元測試

但是安裝後它可能還不能很好的工作,因為 vscode-jest 暫時並不知道我們的 jest 配置檔案在哪裡。

你可以選用下面任意一種方式解決這個問題:

1。 修改 vscode 配置檔案,將 jest。pathToConfig 指向我們剛才編寫的配置檔案

一篇文章學會 Vue 專案單元測試

2。 將 jest 配置寫在 package。json 中的 jest 欄位

3。 將 jest 配置檔案提到專案根目錄,並且更名為 jest。config。js 或者 jest。json

現在 vs-code-jest 會根據 git 修改記錄自動執行應該執行的測試檔案,並在控制檯實時給出測試結果。至此第一部分大功告成。

一篇文章學會 Vue 專案單元測試

第二部分:使用 vue-test-util 提高測試編碼效率

因為 vue-test-util 的官方文件寫的實在是太好了,不再贅述其 API,重點說明一點,為什麼推薦使用 vue-test-util 來編寫 Vue 元件單元測試,因為它不僅提供了很多實用的 API ,還同步了 DOM 的更新,也就是說我們的測試程式碼裡不會再充斥著 vm。$nextTick() 等程式碼,舉個例子

有一個元件

// 。/src/components/hello-world/hello-world。vue

<

template

>

<

div

>

<

h1

>

{{

msg

}}

<

/h1>

<

button

@

click

=

“onClick”

>

click

me

<

/button>

<

/div>

<

/template>

<

script

>

export

default

{

name

‘HelloWorld’

data

()

{

return

{

msg

‘Hello Jest’

};

},

methods

{

onClick

()

{

this

msg

=

‘new message’

},

},

};

<

/script>

對該元件進行測試

// 。/src/components/hello-world/hello-world。spec。js

import

{

shallowMount

}

from

‘@vue/test-utils’

import

HelloWorld

from

‘。/hello-world’

describe

()

=>

{

const

wrapper

=

shallowMount

HelloWorld

);

it

“update ‘msg’ correctly”

()

=>

{

// 點選 button

wrapper

find

‘button’

)。

trigger

‘click’

);

// 可以立即獲取 msg 最新的值,不再需要 wrapper。vm。$nextTick();

expect

wrapper

find

‘h1’

)。

text

())

toEqual

‘new message’

);

});

});

如果需要做一些全域性的 vue-test-util 的配置,可以在 setup。js 裡指定,比如在每個元件例項化時候注入一個 GLOBAL 物件。

// 。/test/unit/setup。js

import

Vue

from

‘vue’

import

{

config

}

from

‘@vue/test-utils’

Vue

config

productionTip

=

false

// provide 的模擬

config

provide

GLOBAL

=

{

logined

false

};

有一個元件注入了 GLOBAL 物件

// 。/src/components/hello-world/hello-world。vue

<

template

>

<

div

>

<

h1

v

-

show

=

“GLOBAL。logined”

>

{{

msg

}}

<

/h1>

<

/div>

<

/template>

<

script

>

export

default

{

name

‘HelloWorld’

inject

‘GLOBAL’

],

data

()

{

return

{

msg

‘Hello Jest’

};

},

};

<

/script>

對該元件進行測試

// 。/src/components/hello-world/hello-world。spec。js

import

{

shallowMount

}

from

‘@vue/test-utils’

import

HelloWorld

from

‘。/hello-world’

describe

()

=>

{

const

wrapper

=

shallowMount

HelloWorld

);

it

‘should render correct contents’

()

=>

{

expect

wrapper

find

‘h1’

)。

isVisible

())。

toBe

false

);

});

});

第三部分: 複雜場景下的測試

1.元件發起了API 請求,我只想知道它發沒發,不想讓它真實發出去。

有一個元件在會在 created 時候發起一個 http 請求

// 。/src/components/user-info/user-info。vue

<

template

>

<

div

class

=

“user-info”

>

<

div

class

=

“name”

>

{{

user

name

}}

<

/div>

<

div

class

=

“desc”

>

{{

user

desc

}}

<

/div>

<

/div>

<

/template>

<

script

>

import

UserApi

from

‘。。/。。/apis/user’

export

default

{

name

‘UserInfo’

data

()

{

return

{

user

{},

};

},

created

()

{

UserApi

getUserInfo

()

then

((

user

=>

{

this

user

=

user

});

},

};

<

/script>

API 介面如下

// 。/src/apis/user。js

function

getUserInfo

()

{

return

$http

get

‘/user’

);

}

export

default

{

getUserInfo

};

對該元件進行測試

// 。/src/components/user-info/user-info。spec。js

import

{

shallowMount

}

from

‘@vue/test-utils’

import

UserInfo

from

‘。/user-info’

import

UserApi

from

‘。。/。。/apis/user’

// mock 掉 user 模組

jest

mock

‘。。/。。/apis/user’

);

// 指定 getUserInfo 方法返回假資料

UserApi

getUserInfo

mockResolvedValue

({

name

‘olive’

desc

‘software engineer’

});

describe

()

=>

{

const

wrapper

=

shallowMount

UserInfo

);

test

‘getUserInfo 有且只 call 了一次’

()

=>

{

expect

UserApi

getUserInfo

mock

calls

length

)。

toBe

1

);

});

it

‘使用者資訊渲染正確’

()

=>

{

expect

wrapper

find

‘。name’

)。

text

())。

toEqual

‘olive’

);

expect

wrapper

find

‘。desc’

)。

text

())。

toEqual

‘software engineer’

);

});

});

2.簡單的 A 元件依賴了一個複雜的 B 元件,但是我只想測試 A 的邏輯,不想拉起 B 的邏輯。

這種場景其實很常見,比如某些複雜元件 import 了某些會自執行的程式碼,這個時候為了保證單元測試的純粹,我們應該忽略掉所依賴的子元件的邏輯。

// 。/src/components/simple/simple。vue

<

template

>

<

div

>

<

div

class

=

“header”

>

{{

msg

}}

<

/div>

<

div

>

<

complex

><

/complex>

// 即使 vue-test-util 可以透過存根的方式將這個元件渲染為 complex-stub

// 但其內部的其他程式碼可能依然被執行

<

/div>

<

/div>

<

/template>

<

script

>

import

Complex

from

‘。/children/complex’

export

default

{

name

‘Simple’

data

()

{

return

{

msg

‘simple’

};

},

components

{

Complex

},

};

<

/script>

對該元件進行測試

// 。/src/components/simple/simple。spec。js

import

{

shallowMount

}

from

‘@vue/test-utils’

import

Simple

from

‘。/simple’

// 攔截掉 。vue 檔案的內容

jest

mock

‘。/children/complex。vue’

()

=>

({

render

h

{

h

();

},

}));

describe

()

=>

{

const

wrapper

=

shallowMount

Simple

{

stubs

‘user-info’

],

});

it

‘文字渲染正確’

()

=>

{

expect

wrapper

find

‘。header’

)。

text

())。

toEqual

‘simple’

);

});

});

3. 測試 Rx.js

假設有一個 subject,訂閱的時候會發射 ‘hello-rx’

// 。/src/components/rx-demo/msg。stream。js

import

{

BehaviorSubject

}

from

‘rxjs’

const

msg$$

=

new

BehaviorSubject

‘hello-rx’

);

export

default

msg$$

有一個元件訂閱該 subject

// 。/src/components/rx-demo/rx-demo。vue

<

template

>

<

div

class

=

“rx-demo”

>

{{

msg

}}

<

/div>

<

/template>

<

script

>

import

msg$$

from

‘。/msg。stream’

export

default

{

name

‘RxDemo’

data

()

{

return

{

msg

‘’

};

},

created

()

{

msg$$

subscribe

((

res

=>

{

this

msg

=

res

});

},

};

<

/script>

對該元件進行測試

// 。/test/utils/index。js

import

{

BehaviorSubject

}

from

‘rxjs’

function

mockSubject

data

{

return

new

BehaviorSubject

data

);

}

export

{

mockSubject

};

// 。/src/components/rx-demo/rx-demo。spec。js

import

{

shallowMount

}

from

‘@vue/test-utils’

import

RxDemo

from

‘。/rx-demo’

jest

mock

‘。/msg。stream。js’

()

=>

{

//這一部分會被 babel-jest 提升到程式碼頂部,所以需要這麼動態去 require 才可以保證程式碼順序是正確的

const

{

mockSubject

}

=

require

‘。。/。。/。。/test/unit/util’

);

return

mockSubject

‘mock-data’

);

});

describe

()

=>

{

it

‘Rx 訂閱成功,文字渲染正確’

()

=>

{

const

wrapper

=

shallowMount

RxDemo

);

expect

wrapper

find

‘。rx-demo’

)。

text

())。

toEqual

‘mock-data’

);

});

});

總得來說,把測試目標元件範圍外的不好測試的模組全部 mock 掉,然後再根據你們對單元測試要求的細粒度進行斷言。

其他簡單場景不再做贅述,遇到問題請隨時問我。謝謝。

參考資料:

專案地址

Vue Test Utils

Jest

Rxjs

標簽: vue  jest  test  user  src