--- title: Use Vitest for Vue 3 Testing - Recommended by Vue Team impact: MEDIUM impactDescription: Using Jest or other runners with Vite projects requires complex configuration and causes slower test runs type: best-practice tags: [vue3, testing, vitest, vite, configuration, setup] --- # 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 ```bash # 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:** ```javascript 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:** ```json { "scripts": { "test": "vitest", "test:run": "vitest run", "test:coverage": "vitest run --coverage" } } ``` **tsconfig.json (if using TypeScript):** ```json { "compilerOptions": { "types": ["vitest/globals"] } } ``` ## Test File Example ```javascript // 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 ```bash npm install -D @testing-library/vue @testing-library/jest-dom ``` ```javascript // src/test/setup.js import { expect } from 'vitest'; import * as matchers from '@testing-library/jest-dom/matchers'; expect.extend(matchers); ``` ```javascript // 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 ```javascript // 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 ```javascript import { vi } from 'vitest'; vi.mock('@/api/users', () => ({ fetchUser: vi.fn().mockResolvedValue({ name: 'John' }), })); ``` ### Testing with Fake Timers ```javascript 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(); }); ``` ## Reference - [Vitest Documentation](https://vitest.dev/) - [Vue.js Testing Guide](https://vuejs.org/guide/scaling-up/testing) - [Vue Test Utils](https://test-utils.vuejs.org/)