Lesson 22 / 25
Testing With Vitest and Vue Test Utils
Component tests that act like a user.
Mount, interact, assert
Vitest is a Vite-native test runner with a Jest-compatible API (describe, it, expect, vi.fn()), and Vue Test Utils (@vue/test-utils) mounts components in a simulated DOM such as jsdom or happy-dom. A component test mounts the component with props, finds elements, triggers events, waits for Vue to update (await trigger(...) returns a promise) and asserts on rendered text or emitted events via wrapper.emitted(). Test behaviour the user sees rather than internal details. Composables and Pinia stores can be tested as plain functions; @pinia/testing provides createTestingPinia for components that use stores. Use end-to-end tools such as Playwright or Cypress for full user journeys.
Tested, fast and ready to ship
Tests, performance tools and server rendering options take a Vue app from working to production-ready.
Testing a counter component
Props, interaction and emitted events.
// Counter.spec.js
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import Counter from './Counter.vue'
describe('Counter', () => {
it('renders the initial count from props', () => {
const wrapper = mount(Counter, { props: { start: 5 } })
expect(wrapper.text()).toContain('Count: 5')
})
it('increments and emits change when clicked', async () => {
const wrapper = mount(Counter, { props: { start: 0 } })
await wrapper.find('button').trigger('click')
expect(wrapper.text()).toContain('Count: 1')
expect(wrapper.emitted('change')).toHaveLength(1)
expect(wrapper.emitted('change')[0]).toEqual([1])
})
})Await DOM updates
Vue updates the DOM asynchronously. Await trigger, setValue or nextTick() before asserting, or your test will see the old output.
Quick check: Why is wrapper.find(...).trigger("click") awaited in a test?
- Clicks are simulated with a delay of one second
- trigger sends a network request
- Vitest requires every call to be awaited
- Vue updates the DOM asynchronously, and the promise resolves after the update
Answer
Vue updates the DOM asynchronously, and the promise resolves after the update — Awaiting lets the re-render finish.