963 markdown files reformatted with the repository's pinned prettier so pnpm format:check covers the folded tree like every other repo file. The formatter's embedded-language pass also normalized code fences (TS semicolons, closed HTML tags in examples, lowercased CSS hex colors, one renumbered list that skipped an index). Alphanumeric token deltas vs the fold commit were audited file-by-file; all are formatter-equivalent markup normalizations plus the four sanitized skills.
5.2 KiB
5.2 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Use Vitest for Vue 3 Testing - Recommended by Vue Team | MEDIUM | Using Jest or other runners with Vite projects requires complex configuration and causes slower test runs | best-practice |
|
Use Vitest for Vue 3 Testing - Recommended by Vue Team
Impact: MEDIUM - Vitest is created and maintained by Vue/Vite team members and shares the same configuration and transform pipeline as Vite. Using Jest or other test runners with Vite-based projects requires additional configuration and can result in slower test execution and compatibility issues.
Use Vitest for new Vue 3 projects. Only consider Jest if migrating an existing test suite.
Task Checklist
- Install Vitest and related packages for Vue testing
- Configure vitest in vite.config.js or vitest.config.js
- Set up proper test environment (happy-dom or jsdom)
- Add test scripts to package.json
- Configure globals if desired for cleaner test syntax
- Use @vue/test-utils for component mounting
Quick Setup
# Install required packages
npm install -D vitest @vue/test-utils happy-dom
# or with jsdom
npm install -D vitest @vue/test-utils jsdom
vite.config.js:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
test: {
// Enable global test APIs (describe, it, expect)
globals: true,
// Use happy-dom for faster tests (or 'jsdom' for better compatibility)
environment: 'happy-dom',
// Optional: Setup files for global configuration
setupFiles: ['./src/test/setup.js'],
},
});
package.json:
{
"scripts": {
"test": "vitest",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage"
}
}
tsconfig.json (if using TypeScript):
{
"compilerOptions": {
"types": ["vitest/globals"]
}
}
Test File Example
// src/components/Counter.test.js
import { describe, it, expect, beforeEach } from 'vitest'; // optional with globals: true
import { mount } from '@vue/test-utils';
import Counter from './Counter.vue';
describe('Counter', () => {
let wrapper;
beforeEach(() => {
wrapper = mount(Counter);
});
it('renders initial count', () => {
expect(wrapper.find('[data-testid="count"]').text()).toBe('0');
});
it('increments when button clicked', async () => {
await wrapper.find('[data-testid="increment"]').trigger('click');
expect(wrapper.find('[data-testid="count"]').text()).toBe('1');
});
});
Vitest vs Jest Comparison
| Feature | Vitest | Jest |
|---|---|---|
| Vite Integration | Native | Requires config |
| Speed | Very fast (ESM native) | Slower with Vite |
| Watch Mode | Excellent | Good |
| Vue SFC Support | Works with Vite | Needs vue-jest |
| Config Sharing | Same as vite.config | Separate |
| API | Jest-compatible | Standard |
Using with Testing Library
npm install -D @testing-library/vue @testing-library/jest-dom
// src/test/setup.js
import { expect } from 'vitest';
import * as matchers from '@testing-library/jest-dom/matchers';
expect.extend(matchers);
// Component.test.js
import { render, screen, fireEvent } from '@testing-library/vue';
import UserCard from './UserCard.vue';
test('displays user name', () => {
render(UserCard, {
props: { name: 'John Doe' },
});
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
Advanced Configuration
// vitest.config.js (separate file if preferred)
import { defineConfig } from 'vitest/config';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
test: {
globals: true,
environment: 'happy-dom',
include: ['**/*.{test,spec}.{js,ts,jsx,tsx}'],
exclude: ['node_modules', 'dist', 'e2e'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: ['node_modules', 'test'],
},
// Helpful for debugging
reporters: ['verbose'],
// Run tests in sequence in CI
poolOptions: {
threads: {
singleThread: process.env.CI === 'true',
},
},
},
});
Common Patterns
Mocking Modules
import { vi } from 'vitest';
vi.mock('@/api/users', () => ({
fetchUser: vi.fn().mockResolvedValue({ name: 'John' }),
}));
Testing with Fake Timers
import { vi, beforeEach, afterEach } from 'vitest';
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.restoreAllMocks();
});
test('debounced search', async () => {
const wrapper = mount(SearchBox);
await wrapper.find('input').setValue('vue');
vi.advanceTimersByTime(300);
await flushPromises();
expect(wrapper.emitted('search')).toBeTruthy();
});