测试存储商店
按照设计,商店将在许多地方使用,并且会使测试变得比应有的困难得多。 幸运的是,情况并非如此。 我们在测试 store 时需要注意三件事:
pinia
实例:没有它,商店就无法运作actions
:大多数时候,它们包含我们商店最复杂的逻辑。 如果他们默认被嘲笑不是很好吗?- 插件:如果你依赖插件,你也必须安装它们进行测试
根据您测试的内容或方式,我们需要以不同的方式处理这三个问题:
对商店进行单元测试
要对商店进行单元测试,最重要的部分是创建一个 pinia
实例:
js
// stores/counter.spec.ts
import { setActivePinia, createPinia } from 'pinia'
import { useCounter } from '../src/stores/counter'
describe('Counter Store', () => {
beforeEach(() => {
// 创建一个新的 pinia 并使其处于活动状态,因此任何 useStore() 调用
// 都会自动拾取它,而无需将其传递给它:`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)
})
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
如果你有任何商店插件,有一件重要的事情要知道:只有在 App 中安装了 pinia
后才会使用插件。 这可以通过创建一个空的 App 或一个假的 App 来解决:
js
import { setActivePinia, createPinia } from 'pinia'
import { createApp } from 'vue'
import { somePlugin } from '../src/stores/plugin'
// same code as above...
// 您无需为每次测试创建一个应用程序
const app = createApp({})
beforeEach(() => {
const pinia = createPinia().use(somePlugin)
app.use(pinia)
setActivePinia(pinia)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
单元测试组件
这可以通过createTestingPinia()
来实现,它返回一个旨在帮助单元测试组件的pinia 实例。
首先安装@pinia/testing
:
shell
npm i -D @pinia/testing
1
并确保在安装组件时在您的测试中创建一个测试松果:
js
import { mount } from '@vue/test-utils'
import { createTestingPinia } from '@pinia/testing'
// 导入您想在测试中与之交互的任何商店
import { useSomeStore } from '@/stores/myStore'
const wrapper = mount(Counter, {
global: {
plugins: [createTestingPinia()],
},
})
const store = useSomeStore() // 使用测试pinia!
// 可以直接操作状态
store.name = 'my new name'
// 也可以通过patch来完成
store.$patch({ name: 'new name' })
expect(store.name).toBe('new name')
// 默认情况下,操作是存根的,这意味着默认情况下它们不会执行其代码。
// 请参阅下文以自定义此行为。
store.someAction()
expect(store.someAction).toHaveBeenCalledTimes(1)
expect(store.someAction).toHaveBeenLastCalledWith()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
请注意,如果您使用的是 Vue 2,@vue/test-utils
需要 略有不同的配置。
状态初始化
在创建测试松果时,您可以通过传递 initialState
对象来设置所有商店的初始状态。 该对象将在创建时被测试松果用于 patch 存储。 假设您要初始化此商店的状态:
ts
import { defineStore } from 'pinia'
const useCounterStore = defineStore('counter', {
state: () => ({ n: 0 }),
// ...
})
1
2
3
4
5
6
2
3
4
5
6
由于 store 被命名为 "counter",所以需要在 initialState
中添加一个匹配的对象:
ts
// somewhere in your test
const wrapper = mount(Counter, {
global: {
plugins: [
createTestingPinia({
initialState: {
counter: { n: 20 }, // start the counter at 20 instead of 0
},
}),
],
},
})
const store = useSomeStore() // uses the testing pinia!
store.n // 20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
自定义动作的行为
除非另有说明,否则createTestingPinia
会排除所有商店操作。 这允许您单独测试组件和存储。
如果您想恢复此行为并在测试期间正常执行您的操作,请在调用 createTestingPinia
时指定 stubActions: false
:
js
const wrapper = mount(Counter, {
global: {
plugins: [createTestingPinia({ stubActions: false })],
},
})
const store = useSomeStore()
// 现在这个调用将执行商店定义的实现
store.someAction()
// ...但它仍然被间谍包裹着,所以你可以检查电话
expect(store.someAction).toHaveBeenCalledTimes(1)
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
指定CreateSpy函数
当使用 Jest 或带有 globals: true
的 vitest 时,createTestingPinia
会使用基于现有测试框架(jest.fn
或 vitest.fn
)的 spy 函数自动存根操作。 如果您使用不同的框架,则需要提供 createSpy 选项:
js
import sinon from 'sinon'
createTestingPinia({
createSpy: sinon.spy, // 使用 sinon 的spy来包装动作
})
1
2
3
4
5
2
3
4
5
您可以在测试包的测试中找到更多示例。
Mocking getters
默认情况下,任何 getter 都将像常规用法一样计算,但您可以通过将 getter 设置为您想要的任何值来手动强制一个值:
ts
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 // 🪄 getters are writable only in tests
// 设置为 undefined 以重置默认行为
// @ts-expect-error: usually it's a number
counter.double = undefined
counter.double // 2 (=1 x 2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Pinia 插件
如果您有任何 pinia 插件,请确保在调用 createTestingPinia()
时传递它们,以便正确应用它们。 不要使用 testingPinia.use(MyPlugin)
添加它们,就像使用普通 pinia 一样:
js
import { createTestingPinia } from '@pinia/testing'
import { somePlugin } from '../src/stores/plugin'
// inside some test
const wrapper = mount(Counter, {
global: {
plugins: [
createTestingPinia({
stubActions: false,
plugins: [somePlugin],
}),
],
},
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
E2E tests
对于 pinia,您无需为 e2e 测试进行任何更改,这就是 e2e 测试的重点! 您也许可以测试 HTTP 请求,但这超出了本指南的范围😄。
单元测试组件 (Vue 2)
使用 Vue Test Utils 1 时,将 Pinia 安装在 localVue
上:
js
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() // uses the testing pinia!
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13