V
Vue + Pinia 单元测试
作者:鹿Sir开发工具v1
为 Vue 3 + TypeScript + Vitest + Pinia 代码库编写和评审单元测试,覆盖组件、组合式函数与 store 三类对象,规范 createTestingPinia 的使用姿势与 Vue Test Utils 模式,坚持黑盒断言、避免耦合实现细节。当用户需要编写或评审组件/store 单元测试、搭建 Vitest 测试、mock Pinia 时触发。触发词:单元测试、Vue 测试、Vitest、Pinia 测试、测试评审。
下载量
343
点赞
83
价格
免费
技能文档
---
name: github-unit-test-vue-pinia
title: Vue + Pinia 单元测试
category: 开发工具
description: 为 Vue 3 + TypeScript + Vitest + Pinia 代码库编写和评审单元测试,覆盖组件、组合式函数与 store 三类对象,规范 createTestingPinia 的使用姿势与 Vue Test Utils 模式,坚持黑盒断言、避免耦合实现细节。当用户需要编写或评审组件/store 单元测试、搭建 Vitest 测试、mock Pinia 时触发。触发词:单元测试、Vue 测试、Vitest、Pinia 测试、测试评审。
---
# Vue + Pinia 单元测试
为 Vue 组件、组合式函数与 Pinia store 创建或评审单元测试,保持测试短小、确定、行为优先。
## 技能工作流
### 步骤1:确定行为边界
先明确要测试的行为边界属于哪一类:组件 UI 行为、组合式函数行为,还是 store 行为。
### 步骤2:选择最窄的测试方式
选择能够证明该行为的最小测试样式,不引入多余的挂载深度或真实依赖。
### 步骤3:搭建 Pinia 测试环境
用覆盖场景所需的最小能力项配置 Pinia(详见下方「Pinia 测试要点」)。
### 步骤4:通过公开输入驱动测试
通过 props、表单更新、按钮点击、子组件事件、store API 等公开输入触发行为。
### 步骤5:断言可观察输出
优先断言可观察的输出与副作用(渲染文本、触发的事件、回调调用、store 状态变化),确认之后再考虑实例级断言。
### 步骤6:命名与收尾
用清晰的行为化名称命名测试,并指出尚存的覆盖缺口。
## 核心规则
- 每个测试只验证一个行为。
- 优先断言可观察的输入/输出行为(渲染文本、事件、回调、store 状态)。
- 避免与实现细节耦合的断言。
- 仅在没有合理的 DOM/props/事件/store 级断言时才访问 `wrapper.vm`,并视为例外。
- 在 `beforeEach()` 中显式初始化,每个测试重置 mock。
- 标准 Pinia 测试写法以 `references/pinia-patterns.md` 为本地事实来源。
## Pinia 测试要点
优先使用 `references/pinia-patterns.md`,本地样例覆盖不了的场景再查 Pinia 官方测试手册。
### 组件测试默认写法
挂载时把 `createTestingPinia` 作为全局插件注入,默认使用 `createSpy: vi.fn`,保持一致且便于断言 action 调用:
```ts
const wrapper = mount(ComponentUnderTest, {
global: {
plugins: [
createTestingPinia({
createSpy: vi.fn,
}),
],
},
});
```
默认情况下 action 被替身化并附加侦听。测试只需要验证某个 action 被调用(或未被调用)时,保持 `stubActions: true`(默认值)。
### 可接受的最小 Pinia 配置
以下写法同样正确,评审时不应判为错误:
- 测试不断言 action 侦听行为时,可用 `createTestingPinia({})`。
- 只需要预置状态或替身化 action、不检查生成的 spy 时,可用不带 `createSpy` 的 `createTestingPinia({ initialState: ... })` 或 `createTestingPinia({ stubActions: ... })`。
- 不挂载组件、只测 store/组合式函数,且需要 mock 或预置依赖 store 时,可用 `setActivePinia(createTestingPinia(...))`。
只有当 action 侦听断言本身就是测试意图时才使用 `createSpy: vi.fn`。
### 仅在必要时执行真实 action
只有测试必须验证 action 的真实行为与副作用时才使用 `stubActions: false`;简单的「是否被调用」断言不要打开它。
```ts
const wrapper = mount(ComponentUnderTest, {
global: {
plugins: [
createTestingPinia({
createSpy: vi.fn,
stubActions: false,
}),
],
},
});
```
### 用 `initialState` 预置 store 状态
```ts
const wrapper = mount(ComponentUnderTest, {
global: {
plugins: [
createTestingPinia({
createSpy: vi.fn,
initialState: {
counter: { n: 20 },
user: { name: "Leia Organa" },
},
}),
],
},
});
```
### 通过 `createTestingPinia` 注入插件
```ts
const wrapper = mount(ComponentUnderTest, {
global: {
plugins: [
createTestingPinia({
createSpy: vi.fn,
plugins: [myPiniaPlugin],
}),
],
},
});
```
### 边界场景的 getter 覆盖
```ts
const pinia = createTestingPinia({ createSpy: vi.fn });
const store = useCounterStore(pinia);
store.double = 999;
// @ts-expect-error test-only reset of overridden getter
store.double = undefined;
```
### 纯 store 单元测试
目标是验证 store 状态流转与 action 行为、且不需要组件渲染时,优先用 `createPinia()` 写纯 store 测试;只有需要替身化依赖 store、预置测试替身或 action 侦听时才用 `createTestingPinia()`。
```ts
beforeEach(() => {
setActivePinia(createPinia());
});
it("increments", () => {
const counter = useCounterStore();
counter.increment();
expect(counter.n).toBe(1);
});
```
## Vue Test Utils 要点
遵循 Vue Test Utils 官方指南:<https://test-utils.vuejs.org/guide/>
- 聚焦单元测试默认浅挂载(shallow)。
- 仅当被测对象是集成行为时才挂载完整组件树。
- 通过 props、拟真交互、事件触发来驱动行为。
- 子组件桩事件优先用 `findComponent(...).vm.$emit(...)`,不触碰父组件内部。
- 仅当更新是异步时才使用 `nextTick`。
- 用 `wrapper.emitted(...)` 断言事件与载荷。
- 只有当 DOM/事件/props/store 级断言都无法表达该行为时才访问 `wrapper.vm`,并保持断言范围最小。
## 关键测试片段
触发并断言事件载荷:
```ts
await wrapper.find("button").trigger("click");
expect(wrapper.emitted("submit")?.[0]?.[0]).toBe("Mango Mission");
```
更新输入并断言输出:
```ts
await wrapper.find("input").setValue("Agent Violet");
await wrapper.find("form").trigger("submit");
expect(wrapper.emitted("save")?.[0]?.[0]).toBe("Agent Violet");
```
## 测试编写流程
1. 确定要测试的行为边界。
2. 构造最小夹具数据(只包含该行为需要的字段)。
3. 配置 Pinia 与必要的测试替身。
4. 通过公开输入触发行为。
5. 断言公开输出与副作用。
6. 把测试名称重构为描述行为而非实现。
## 约束与安全
- 不测试私有/内部实现细节。
- 不对动态 UI 行为滥用快照。
- 大对象中只有一个行为重要时,不断言每一个字段。
- 假数据保持确定性,避免随机值。
- 属于上文「可接受的最小 Pinia 配置」的写法,不得判为错误。
- 除非被测行为确实需要,不把能用的测试重写为更深的挂载或真实 action。
- 评审时明确指出:缺失的覆盖、脆弱的选择器、与实现耦合的断言。
## 输出约定
- 「创建/更新」场景:返回完成的测试代码,并附一句说明所选的 Pinia 策略。
- 「评审」场景:先给出具体发现,再列缺失覆盖与脆弱性风险。
- 最优选择不明确时,说明驱动当前测试配置的假设。
## 参考资料
- `references/pinia-patterns.md`
- Pinia 测试手册:<https://pinia.vuejs.org/zh/cookbook/testing.html>
- Vue Test Utils 指南:<https://test-utils.vuejs.org/guide/>使用说明
# Vue + Pinia 单元测试
为 Vue 3 + TypeScript + Vitest + Pinia 代码库编写和评审单元测试,坚持黑盒断言与行为优先,规范 createTestingPinia 的使用姿势。
## 使用
```text
为这个组件写单元测试:它点击提交按钮后触发 submit 事件并携带表单值,
其中用到了 Pinia store 的 saveData action。
```
输出为可直接落盘的测试代码,附所选 Pinia 策略说明;评审场景则输出具体问题清单与覆盖缺口。
## 工作原理
1. 确定行为边界(组件 / 组合式函数 / store),选择能证明行为的最窄测试样式
2. 按需配置 Pinia:默认 `createTestingPinia({ createSpy: vi.fn })`,仅在必要时执行真实 action 或预置 initialState
3. 通过 props、点击、表单等公开输入驱动行为,断言渲染输出、事件与 store 状态,禁止耦合实现细节支持平台:Qoder · QoderWork · Claude · Codex 等 AI 编程助手