LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 4017 min read

Testing Vue Components

Set up unit tests for Vue components with Vitest and Vue Test Utils, and end-to-end tests with Playwright or Cypress.

Levels of Testing

Unit Tests

Test one component or composable in isolation — fast and focused.

Integration Tests

Test how several components work together, like a form and its parent.

End-to-End Tests

Drive a real browser through your app exactly as a user would.

Setting Up Vitest

Vitest is Vite's companion test runner, and can be selected automatically during project scaffolding.

npm install --save-dev vitest @vue/test-utils jsdom

Mounting a Component with Vue Test Utils

Vue Test Utils' mount() function renders a component in an isolated test environment, giving you a wrapper object to inspect and interact with.

// Counter.test.js
import { mount } from '@vue/test-utils';
import Counter from './Counter.vue';
test('renders the initial count', () => {
const wrapper = mount(Counter, { props: { initialValue: 5 } });
expect(wrapper.text()).toContain('5');
});

Simulating User Interaction

test('increments the count when clicked', async () => {
const wrapper = mount(Counter);
await wrapper.find('button').trigger('click');
expect(wrapper.text()).toContain('1');
});
Always await trigger()

Vue batches DOM updates, so the DOM doesn't reflect a state change instantly — await wrapper.find(...).trigger('click') (or nextTick()) before asserting on the resulting output.

Testing Pinia Stores

Since a Pinia store is essentially a plain composable, you can test its actions and getters directly, or provide a test-specific Pinia instance for components that use one.

import { createPinia, setActivePinia } from 'pinia';
import { useCartStore } from '../stores/cart';
beforeEach(() => {
setActivePinia(createPinia());
});
test('addItem adds an item to the cart', () => {
const cart = useCartStore();
cart.addItem({ id: 1, name: 'Mouse', price: 25 });
expect(cart.items).toHaveLength(1);
});

End-to-End Testing

Playwright or Cypress drive a real browser against your running app, verifying full flows — navigation, form submission — the way an actual visitor would experience them.

import { test, expect } from '@playwright/test';
test('user can add an item to the cart', async ({ page }) => {
await page.goto('/shop');
await page.click('text=Add to Cart');
await expect(page.getByText('1 item in cart')).toBeVisible();
});

FAQs

For most real projects, yes — unit tests catch logic bugs cheaply, while end-to-end tests catch integration issues that only surface in a real browser.

Yes — pass a test router instance (or a stub) via the global.plugins mounting option to provide the routing context a component expects.

Summary

Vitest and Vue Test Utils cover unit and integration testing, with Playwright or Cypress rounding out full end-to-end coverage. Next, you'll look at performance optimization techniques specific to Vue.

Next Lesson →

Performance Optimization