SkillπEnglish
← 返回目录
deepskill

Vue 3、TypeScript、Vitest 与 Pinia 单元测试

unit-test-vue-pinia

授权

结合 createTestingPinia 和 Vue Test Utils 模式,为 Vue 3、TypeScript、Vitest 与 Pinia 编写和审阅以行为为先的单元测试。

↓ 0 下载 ↗ 0 分享点击

介绍来源: author · GitHub, Inc. · b6483db44fb602654bca6a097185fcaddb305f97 参考来源

作者原文

Write and review unit tests for Vue 3 + TypeScript + Vitest + Pinia codebases. Use when creating or updating tests for components, composables, and stores; mocking Pinia with createTestingPinia; applying Vue Test Utils patterns; and enforcing black-box assertions over implementation details.

SkillPi 解读

能完成的任务
选择组件/组合式函数/store 的测试边界,配置 Pinia,并断言可观察的输入、输出和副作用。
适合谁
为 Vue 和 Pinia 代码库编写测试的开发者。
输入和输出
输入被测代码及期望公开行为;输出单元测试、模拟配置及尚存覆盖缺口说明。
软件、账号及付费要求
需要所列 Vue 3/TypeScript/Vitest/Pinia 技术栈、Vue Test Utils 及引用的本地测试模式;未指定付费服务。 使用时由宿主核对并授权实际文件读写和外部服务。文档引用域名(非运行白名单):pinia.vuejs.org, test-utils.vuejs.org
限制与区别
优先观察行为,避免与实现耦合的断言,并逐测试重置模拟。只有指引并不代表测试已经通过。
实测状态
未测试

权限与使用前检查

根据当前主文档自动整理,命中内容可能是示例或防护说明;不是实际权限声明,也未覆盖附件、外部依赖或运行行为。

未从主文档识别到上述常见操作,不能据此判断无需权限或没有风险。

文档引用的域名或地址

这些地址可能用于文档链接或示例,不是已批准的联网清单。使用前核对实际目的地及发送的数据。

  • pinia.vuejs.org
  • test-utils.vuejs.org

本站展示提示不会授予权限。涉及文件改动、密钥、外部发送或费用时,仍由你使用的工具按需确认。

安全评级与访问范围

caution · 静态证据核验,不代表运行安全保证。

需要所列 Vue 3/TypeScript/Vitest/Pinia 技术栈、Vue Test Utils 及引用的本地测试模式;未指定付费服务。 使用时由宿主核对并授权实际文件读写和外部服务。文档引用域名(非运行白名单):pinia.vuejs.org, test-utils.vuejs.org

使用前请由宿主核对并授权实际文件读写、外部服务和联网目的地;本站的核验不会授予任何运行权限。

原始 SKILL.md

---
name: unit-test-vue-pinia
category: testing
description: 'Write and review unit tests for Vue 3 + TypeScript + Vitest + Pinia codebases. Use when creating or updating tests for components, composables, and stores; mocking Pinia with createTestingPinia; applying Vue Test Utils patterns; and enforcing black-box assertions over implementation details.'
---

# unit-test-vue-pinia

Use this skill to create or review unit tests for Vue components, composables, and Pinia stores. Keep tests small, deterministic, and behavior-first.

## Workflow

1. Identify the behavior boundary first: component UI behavior, composable behavior, or store behavior.
2. Choose the narrowest test style that can prove that behavior.
3. Set up Pinia with the least powerful option that still covers the scenario.
4. Drive the test through public inputs such as props, form updates, button clicks, emitted child events, and store APIs.
5. Assert observable outputs and side effects before considering any instance-level assertion.
6. Return or review tests with clear behavior-oriented names and note any remaining coverage gaps.

## Core Rules

- Test one behavior per test.
- Assert observable input/output behavior first (rendered text, emitted events, callback calls, store state changes).
- Avoid implementation-coupled assertions.
- Access `wrapper.vm` only in exceptional cases when there is no reasonable DOM, prop, emit, or store-level assertion.
- Prefer explicit setup in `beforeEach()` and reset mocks every test.
- Use checked-in reference material in `references/pinia-patterns.md` as the local source of truth for standard Pinia test setups.

## Pinia Testing Approach

Use `references/pinia-patterns.md` first, then fall back to Pinia's testing cookbook when the checked-in examples do not cover the case.

### Default pattern for component tests

Use `createTestingPinia` as a global plugin while mounting.
Prefer `createSpy: vi.fn` as the default for consistency and easier action-spy assertions.

```ts
const wrapper = mount(ComponentUnderTest, {
	global: {
		plugins: [
			createTestingPinia({
				createSpy: vi.fn,
			}),
		],
	},
});
```

By default, actions are stubbed and spied.
Use `stubActions: true` (default) when the test only needs to verify whether an action was called (or not called).

### Accepted minimal Pinia setups

The following are also valid and should not be flagged as incorrect:

- `createTestingPinia({})` when the test does not assert Pinia action spy behavior.
- `createTestingPinia({ initialState: ... })` or `createTestingPinia({ stubActions: ... })` without `createSpy`, when the test only needs state seeding or action stubbing behavior and does not inspect generated spies.
- `setActivePinia(createTestingPinia(...))` in store/composable-focused tests (without mounting a component) when mocking/seeding dependent stores is needed.

Use `createSpy: vi.fn` when action spy assertions are part of the test intent.

### Execute real actions only when needed

Use `stubActions: false` only when the test must validate the action's real behavior and side effects. Do not switch it on by default for simple "was called" assertions.

```ts
const wrapper = mount(ComponentUnderTest, {
	global: {
		plugins: [
			createTestingPinia({
				createSpy: vi.fn,
				stubActions: false,
			}),
		],
	},
});
```

### Seed store state with `initialState`

```ts
const wrapper = mount(ComponentUnderTest, {
	global: {
		plugins: [
			createTestingPinia({
				createSpy: vi.fn,
				initialState: {
					counter: { n: 20 },
					user: { name: "Leia Organa" },
				},
			}),
		],
	},
});
```

### Add Pinia plugins through `createTestingPinia`

```ts
const wrapper = mount(ComponentUnderTest, {
	global: {
		plugins: [
			createTestingPinia({
				createSpy: vi.fn,
				plugins: [myPiniaPlugin],
			}),
		],
	},
});
```

### Getter override pattern for edge cases

```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;
```

### Pure store unit tests

Prefer pure store tests with `createPinia()` when the goal is to validate store state transitions and action behavior without component rendering. Use `createTestingPinia()` only when you need stubbed dependent stores, seeded test doubles, or action spies.

```ts
beforeEach(() => {
	setActivePinia(createPinia());
});

it("increments", () => {
	const counter = useCounterStore();
	counter.increment();
	expect(counter.n).toBe(1);
});
```

## Vue Test Utils Approach

Follow Vue Test Utils guidance: <https://test-utils.vuejs.org/guide/>

- Mount shallow by default for focused unit tests.
- Mount full component trees only when integration behavior is the subject.
- Drive behavior through props, user-like interactions, and emitted events.
- Prefer `findComponent(...).vm.$emit(...)` for child stub events instead of touching parent internals.
- Use `nextTick` only when updates are async.
- Assert emitted events and payloads with `wrapper.emitted(...)`.
- Access `wrapper.vm` only when no DOM assertion, emitted event assertion, prop assertion, or store-level assertion can express the behavior. Treat it as an exception and keep the assertion narrowly scoped.

## Key Testing Snippets

Emit and assert payload:

```ts
await wrapper.find("button").trigger("click");
expect(wrapper.emitted("submit")?.[0]?.[0]).toBe("Mango Mission");
```

Update input and assert output:

```ts
await wrapper.find("input").setValue("Agent Violet");
await wrapper.find("form").trigger("submit");
expect(wrapper.emitted("save")?.[0]?.[0]).toBe("Agent Violet");
```

## Test Writing Workflow

1. Identify the behavior boundary to test.
2. Build minimal fixture data (only fields needed by that behavior).
3. Configure Pinia and required test doubles.
4. Trigger behavior through public inputs.
5. Assert public outputs and side effects.
6. Refactor test names to describe behavior, not implementation.

## Constraints and Safety

- Do not test private/internal implementation details.
- Do not overuse snapshots for dynamic UI behavior.
- Do not assert every field in large objects if only one behavior matters.
- Keep fake data deterministic; avoid random values.
- Do not claim a Pinia setup is wrong when it is one of the accepted minimal setups above.
- Do not rewrite working tests toward deeper mounting or real actions unless the behavior under test requires that extra surface area.
- Flag missing test coverage, brittle selectors, and implementation-coupled assertions explicitly during review.

## Output Contract

- For `create` or `update`, return the finished test code plus a short note describing the selected Pinia strategy.
- For `review`, return concrete findings first, then missing coverage or brittleness risks.
- When the safest choice is ambiguous, state the assumption that drove the chosen test setup.

## References

- `references/pinia-patterns.md`
- Pinia testing cookbook: <https://pinia.vuejs.org/cookbook/testing.html>
- Vue Test Utils guide: <https://test-utils.vuejs.org/guide/>

版权与许可声明

MIT License

Copyright GitHub, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.