從設計上來說,許多地方都會使用 store,所以可能比正常情況更難測試。但幸運的是,這不一定是真的。在測試 store 時,我們需要注意三件事:
pinia
實例:沒有它,store 不能正常工作actions
:大多數(shù)時候,它們包含了 store 最復雜的邏輯。如果它們默認就可以被 mocked,那不是很好嗎?
要對一個 store 進行單元測試,最重要的是創(chuàng)建一個 pinia
實例:
// stores/counter.spec.ts
import { setActivePinia, createPinia } from 'pinia'
import { useCounter } from '../src/stores/counter'
describe('Counter Store', () => {
beforeEach(() => {
// 創(chuàng)建一個新 pinia,并使其處于激活狀態(tài),這樣它就會被任何 useStore() 調(diào)用自動接收
// 而不需要手動傳遞:
// `useStore(pinia)`
setActivePinia(createPinia())
})
it('increments', () => {
const counter = useCounter()
expect(counter.n).toBe(0)
counter.increment()
expect(counter.n).toBe(1)
})
it('increments by amount', () => {
const counter = useCounter()
counter.increment(10)
expect(counter.n).toBe(10)
})
})
如果你有使用任何 store 的插件,有一件重要的事情需要了解:在 pinia
被安裝在一個應用之后,插件才會被使用??梢酝ㄟ^創(chuàng)建一個空的或假的應用來解決這個問題:
import { setActivePinia, createPinia } from 'pinia'
import { createApp } from 'vue'
import { somePlugin } from '../src/stores/plugin'
// 和前面一樣的代碼...
// 測試前你不需要創(chuàng)建應用
const app = createApp({})
beforeEach(() => {
const pinia = createPinia().use(somePlugin)
app.use(pinia)
setActivePinia(pinia)
})
這可以通過 createTestingPinia()
實現(xiàn),它會返回一個僅用于幫助對組件單元測試的 pinia 實例。
從安裝 @pinia/testing
開始:
npm i -D @pinia/testing
確保掛在組件時,在你的測試中創(chuàng)建一個用于測試的 pinia 實例:
import { mount } from '@vue/test-utils'
import { createTestingPinia } from '@pinia/testing'
// 引入任何你想要測試的 store
import { useSomeStore } from '@/stores/myStore'
const wrapper = mount(Counter, {
global: {
plugins: [createTestingPinia()],
},
})
const store = useSomeStore() // // 使用 pinia 的測試實例!
// 可直接操作 state
store.name = 'my new name'
// 也可以通過 patch 來完成
store.$patch({ name: 'new name' })
expect(store.name).toBe('new name')
// action 默認是存根的(stubbed),意味著它們默認不執(zhí)行其代碼。
// 請看下面的內(nèi)容來定制這一行為。
store.someAction()
expect(store.someAction).toHaveBeenCalledTimes(1)
expect(store.someAction).toHaveBeenLastCalledWith()
請注意,如果你使用的是 Vue 2,@vue/test-utils
需要一個輕微不同的配置
在創(chuàng)建測試 Pinia 時,你可以通過傳遞一個 initialState
對象來設置所有 store 的初始狀態(tài)。這個對象將被 pinia 的測試實例用于創(chuàng)建 store 時 patch store。比方說,你想初始化這個 store 的狀態(tài):
import { defineStore } from 'pinia'
const useCounterStore = defineStore('counter', {
state: () => ({ n: 0 }),
// ...
})
由于 store 的名字是 "counter",所以你需要傳遞相應的對象給 initialState
:
// 在測試中的某處
const wrapper = mount(Counter, {
global: {
plugins: [
createTestingPinia({
initialState: {
counter: { n: 20 }, //從 20 開始計數(shù),而不是 0
},
}),
],
},
})
const store = useSomeStore() // 使用 pinia 的測試實例!
store.n // 20
除非另有指示,createTestingPinia
會存根 (stub) 出所有的 store action。這樣可以讓你獨立測試你的組件和 store。
如果你想恢復這種行為,并在測試中正常執(zhí)行 action,請在調(diào)用 createTestingPinia
時指定 stubActions: false
:
const wrapper = mount(Counter, {
global: {
plugins: [createTestingPinia({ stubActions: false })],
},
})
const store = useSomeStore()
// 現(xiàn)在,這個調(diào)用將由 store 定義的實現(xiàn)執(zhí)行。
store.someAction()
// ...但它仍然被一個 spy 包裝著,所以你可以檢查調(diào)用
expect(store.someAction).toHaveBeenCalledTimes(1)
當使用 Jest,或 vitest 且設置 globals: true
時,createTestingPinia
會自動使用現(xiàn)有測試框架 (jest.fn
或 vitest.fn
) 的 spy 函數(shù)存根 (stub) action。如果你使用的是不同的框架,你需要提供一個 createSpy 選項:
import sinon from 'sinon'
createTestingPinia({
createSpy: sinon.spy, // 使用 sinon's spy 包裝 action
})
你可以在測試包的測試源碼中找到更多的例子。
默認情況下,任何 getter 都會像常規(guī)用法一樣進行計算,但你可以通過將 getter 設置為任何你想要的值來手動強制計算:
import { defineStore } from 'pinia'
import { createTestingPinia } from '@pinia/testing'
const useCounter = defineStore('counter', {
state: () => ({ n: 1 }),
getters: {
double: (state) => state.n * 2,
},
})
const pinia = createTestingPinia()
const counter = useCounter(pinia)
counter.double = 3 // ???? getter 僅在測試中可被重寫
// 設置為 undefined,以重置默認行為
// @ts-expect-error: usually it's a number
counter.double = undefined
counter.double // 2 (=1 x 2)
如果你有使用任何 pinia 插件,確保在調(diào)用 createTestingPinia()
時傳入它們,這樣它們就會被正確加載。不要使用 testingPinia.use(MyPlugin)
來加載它們,而應該像正常的 pinia 那樣:
import { createTestingPinia } from '@pinia/testing'
import { somePlugin } from '../src/stores/plugin'
// 某些測試
const wrapper = mount(Counter, {
global: {
plugins: [
createTestingPinia({
stubActions: false,
plugins: [somePlugin],
}),
],
},
})
對于 pinia,你不需要為端到端測試修改任何代碼,這就是端到端測試的含義!也許你想測試 HTTP 請求,但這已經(jīng)超出了本指南的范圍????。
當你使用的是 Vue Test Utils 1 時,請將 Pinia 安裝在 localVue
上:
import { PiniaVuePlugin } from 'pinia'
import { createLocalVue, mount } from '@vue/test-utils'
import { createTestingPinia } from '@pinia/testing'
const localVue = createLocalVue()
localVue.use(PiniaVuePlugin)
const wrapper = mount(Counter, {
localVue,
pinia: createTestingPinia(),
})
const store = useSomeStore() // 使用 pinia 的測試實例!
更多建議: