Unit, Component, E2E — All Supported
Testing — Vitest & @nuxt/test-utils
@nuxt/test-utils provides a Nuxt-aware test environment; Vitest runs the tests. Together they cover unit, component, and end-to-end testing.
What you'll learn
- Install Vitest and @nuxt/test-utils
- Use mountSuspended for component tests
- Use the setup helper with the server option for end-to-end tests
Testing a Nuxt app requires the Nuxt context — auto-imports, composables,
runtime config. @nuxt/test-utils boots a real Nuxt instance for each test
file, so your components run the same way they do in dev.
Install
npm i -D vitest @nuxt/test-utils @vue/test-utils happy-dom Add a vitest.config.ts:
import { defineVitestConfig } from '@nuxt/test-utils/config'
export default defineVitestConfig({
test: { environment: 'nuxt' },
}) Component Tests with mountSuspended
mountSuspended waits for async setup (top-level await useFetch) before
assertions run.
import { describe, it, expect } from 'vitest'
import { mountSuspended } from '@nuxt/test-utils/runtime'
import PostCard from '~/components/PostCard.vue'
describe('PostCard', () => {
it('renders the title', async () => {
const wrapper = await mountSuspended(PostCard, {
props: { post: { id: 1, title: 'Hello' } },
})
expect(wrapper.text()).toContain('Hello')
})
}) End-to-End with $fetch
setup({ server: true }) boots Nuxt as a real HTTP server. Then $fetch hits
any route or API.
import { describe, it, expect } from 'vitest'
import { setup, $fetch } from '@nuxt/test-utils/e2e'
describe('app', async () => {
await setup({ server: true })
it('renders the home page', async () => {
const html = await $fetch('/')
expect(html).toContain('<h1>Welcome</h1>')
})
it('returns posts from the api', async () => {
const posts = await $fetch('/api/posts')
expect(posts).toHaveLength(3)
})
}) Browser Tests with Playwright
For real click-and-type flows, layer Playwright on top — Nuxt test-utils handles the server, Playwright drives Chromium.
import { test, expect } from '@playwright/test'
import { setup, url } from '@nuxt/test-utils/e2e'
test.beforeAll(async () => { await setup({ server: true }) })
test('login flow', async ({ page }) => {
await page.goto(url('/login'))
await page.fill('input[name=email]', 'a@b.com')
await page.click('button[type=submit]')
await expect(page).toHaveURL(/dashboard/)
})