# Testing With Vitest and Vue Test Utils — Vue.js

Source: https://www.skillbyai.com/en/vue/x-testing

> 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.

![Four ideas: testing, performance, Nuxt and SSR, a release checklist.](assets/figures/vue/section-8-map.svg) — Figure 8.1 — Testing, performance, SSR and checklist.

## Testing a counter component

Props, interaction and emitted events.

```javascript
// 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.

**Quiz:** 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
- [x] 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.
