Pinia store 測(cè)試

2023-09-28 15:27 更新

從設(shè)計(jì)上來說,許多地方都會(huì)使用 store,所以可能比正常情況更難測(cè)試。但幸運(yùn)的是,這不一定是真的。在測(cè)試 store 時(shí),我們需要注意三件事:

  • pinia 實(shí)例:沒有它,store 不能正常工作
  • actions:大多數(shù)時(shí)候,它們包含了 store 最復(fù)雜的邏輯。如果它們默認(rèn)就可以被 mocked,那不是很好嗎?
  • 插件:如果你依賴插件,你也必須為測(cè)試安裝它們

對(duì) store 進(jìn)行單元測(cè)試

要對(duì)一個(gè) store 進(jìn)行單元測(cè)試,最重要的是創(chuàng)建一個(gè) pinia 實(shí)例:

// stores/counter.spec.ts
import { setActivePinia, createPinia } from 'pinia'
import { useCounter } from '../src/stores/counter'


describe('Counter Store', () => {
  beforeEach(() => {
    // 創(chuàng)建一個(gè)新 pinia,并使其處于激活狀態(tài),這樣它就會(huì)被任何 useStore() 調(diào)用自動(dòng)接收
    // 而不需要手動(dòng)傳遞:
    // `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 被安裝在一個(gè)應(yīng)用之后,插件才會(huì)被使用??梢酝ㄟ^創(chuàng)建一個(gè)空的或假的應(yīng)用來解決這個(gè)問題:

import { setActivePinia, createPinia } from 'pinia'
import { createApp } from 'vue'
import { somePlugin } from '../src/stores/plugin'


// 和前面一樣的代碼...


// 測(cè)試前你不需要?jiǎng)?chuàng)建應(yīng)用
const app = createApp({})
beforeEach(() => {
  const pinia = createPinia().use(somePlugin)
  app.use(pinia)
  setActivePinia(pinia)
})

對(duì)組件單元測(cè)試

這可以通過 createTestingPinia() 實(shí)現(xiàn),它會(huì)返回一個(gè)僅用于幫助對(duì)組件單元測(cè)試的 pinia 實(shí)例。

從安裝 @pinia/testing 開始:

npm i -D @pinia/testing

確保掛在組件時(shí),在你的測(cè)試中創(chuàng)建一個(gè)用于測(cè)試的 pinia 實(shí)例:

import { mount } from '@vue/test-utils'
import { createTestingPinia } from '@pinia/testing'
// 引入任何你想要測(cè)試的 store
import { useSomeStore } from '@/stores/myStore'


const wrapper = mount(Counter, {
  global: {
    plugins: [createTestingPinia()],
  },
})


const store = useSomeStore() // // 使用 pinia 的測(cè)試實(shí)例!


// 可直接操作 state
store.name = 'my new name'
// 也可以通過 patch 來完成
store.$patch({ name: 'new name' })
expect(store.name).toBe('new name')


// action 默認(rèn)是存根的(stubbed),意味著它們默認(rèn)不執(zhí)行其代碼。
// 請(qǐng)看下面的內(nèi)容來定制這一行為。
store.someAction()


expect(store.someAction).toHaveBeenCalledTimes(1)
expect(store.someAction).toHaveBeenLastCalledWith()

請(qǐng)注意,如果你使用的是 Vue 2,@vue/test-utils 需要一個(gè)輕微不同的配置

初始 State

在創(chuàng)建測(cè)試 Pinia 時(shí),你可以通過傳遞一個(gè) initialState 對(duì)象來設(shè)置所有 store 的初始狀態(tài)。這個(gè)對(duì)象將被 pinia 的測(cè)試實(shí)例用于創(chuàng)建 store 時(shí) patch store。比方說,你想初始化這個(gè) store 的狀態(tài):

import { defineStore } from 'pinia'


const useCounterStore = defineStore('counter', {
  state: () => ({ n: 0 }),
  // ...
})

由于 store 的名字是 "counter",所以你需要傳遞相應(yīng)的對(duì)象給 initialState

// 在測(cè)試中的某處
const wrapper = mount(Counter, {
  global: {
    plugins: [
      createTestingPinia({
        initialState: {
          counter: { n: 20 }, //從 20 開始計(jì)數(shù),而不是 0
        },
      }),
    ],
  },
})


const store = useSomeStore() // 使用 pinia 的測(cè)試實(shí)例!
store.n // 20

自定義 action 的行為

除非另有指示,createTestingPinia 會(huì)存根 (stub) 出所有的 store action。這樣可以讓你獨(dú)立測(cè)試你的組件和 store。

如果你想恢復(fù)這種行為,并在測(cè)試中正常執(zhí)行 action,請(qǐng)?jiān)谡{(diào)用 createTestingPinia 時(shí)指定 stubActions: false

const wrapper = mount(Counter, {
  global: {
    plugins: [createTestingPinia({ stubActions: false })],
  },
})


const store = useSomeStore()


// 現(xiàn)在,這個(gè)調(diào)用將由 store 定義的實(shí)現(xiàn)執(zhí)行。
store.someAction()


// ...但它仍然被一個(gè) spy 包裝著,所以你可以檢查調(diào)用
expect(store.someAction).toHaveBeenCalledTimes(1)

指定 createSpy 函數(shù)

當(dāng)使用 Jest,或 vitest 且設(shè)置 globals: true 時(shí),createTestingPinia 會(huì)自動(dòng)使用現(xiàn)有測(cè)試框架 (jest.fnvitest.fn) 的 spy 函數(shù)存根 (stub) action。如果你使用的是不同的框架,你需要提供一個(gè) createSpy 選項(xiàng):

import sinon from 'sinon'


createTestingPinia({
  createSpy: sinon.spy, // 使用 sinon's spy 包裝 action
})

你可以在測(cè)試包的測(cè)試源碼中找到更多的例子。

Mocking getters

默認(rèn)情況下,任何 getter 都會(huì)像常規(guī)用法一樣進(jìn)行計(jì)算,但你可以通過將 getter 設(shè)置為任何你想要的值來手動(dòng)強(qiáng)制計(jì)算:

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 僅在測(cè)試中可被重寫


// 設(shè)置為 undefined,以重置默認(rèn)行為
// @ts-expect-error: usually it's a number
counter.double = undefined
counter.double // 2 (=1 x 2)

Pinia 插件

如果你有使用任何 pinia 插件,確保在調(diào)用 createTestingPinia() 時(shí)傳入它們,這樣它們就會(huì)被正確加載。不要使用 testingPinia.use(MyPlugin) 來加載它們,而應(yīng)該像正常的 pinia 那樣:

import { createTestingPinia } from '@pinia/testing'
import { somePlugin } from '../src/stores/plugin'


// 某些測(cè)試
const wrapper = mount(Counter, {
  global: {
    plugins: [
      createTestingPinia({
        stubActions: false,
        plugins: [somePlugin],
      }),
    ],
  },
})

端到端測(cè)試

對(duì)于 pinia,你不需要為端到端測(cè)試修改任何代碼,這就是端到端測(cè)試的含義!也許你想測(cè)試 HTTP 請(qǐng)求,但這已經(jīng)超出了本指南的范圍????。

對(duì)組件單元測(cè)試(Vue 2)

當(dāng)你使用的是 Vue Test Utils 1 時(shí),請(qǐng)將 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 的測(cè)試實(shí)例!
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)