Testing — Vitest & @nuxt/test-utils

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.

4 min read Level 2/5 #nuxt#testing#vitest
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/)
})
Build & Preview →