Testing basics
A test is code that checks other code: it calls a function or renders a component and compares the result with the expected one. Tests run with one command and quickly show that a change broke something. For Nuxt you use the Vitest test runner and the @nuxt/test-utils package, which runs tests in a Nuxt environment, with auto-imports and plugins.
Installing
npm install -D @nuxt/test-utils vitest @vue/test-utils happy-domvitest runs the tests, @vue/test-utils renders components, and happy-dom stands in for the browser. Create a config file in the project root:
// vitest.config.ts
import { defineVitestConfig } from '@nuxt/test-utils/config'
export default defineVitestConfig({
test: {
environment: 'nuxt'
}
})The nuxt environment gives tests everything the app has: the ~ alias, auto-imports, modules. Add a command to the scripts section of package.json: "test": "vitest".
Testing a function
Let us check formatDate from the composables lesson. Create test/formatDate.test.ts:
// test/formatDate.test.ts
import { describe, expect, it } from 'vitest'
import { formatDate } from '~/utils/formatDate'
describe('formatDate', () => {
it('formats a date in English', () => {
expect(formatDate('2025-03-14')).toBe('March 14, 2025')
})
})describe groups tests, it describes one test, and expect(...).toBe(...) compares the result with the expected value. Run:
npm run testVitest finds the .test.ts files, runs them and prints a green tick and the line 1 passed in the terminal. Vitest keeps running and reruns the tests on every file save; press q to quit.
Testing a component
Nuxt components are rendered with mountSuspended: it can wait for an async <script setup> and knows about auto-imports.
// test/PostCard.test.ts
import { describe, expect, it } from 'vitest'
import { mountSuspended } from '@nuxt/test-utils/runtime'
import PostCard from '~/components/PostCard.vue'
describe('PostCard', () => {
it('shows the post title', async () => {
const wrapper = await mountSuspended(PostCard, {
props: { post: { id: 1, title: 'First post', text: 'Hello, world!' } }
})
expect(wrapper.text()).toContain('First post')
})
})The test renders a card with a post and checks that the title is in its text. If you accidentally delete {{ post.title }} from the PostCard.vue template, the test fails and shows what is missing.
Kinds of tests
| Kind | What it checks | With |
|---|---|---|
| Unit | One function | Vitest |
| Component | One component | Vitest and mountSuspended |
| End-to-end | The whole site in a real browser | @nuxt/test-utils/e2e and Playwright |
End-to-end tests build the app, start a server and open pages, so they are slower. Start with unit and component tests.
Common mistake
Rendering a Nuxt component with the plain mount function from @vue/test-utils, or running tests without environment: 'nuxt'. Auto-imports do not work then, and the test fails with errors like useLikes is not defined, even though the app works. For Nuxt components use mountSuspended and the nuxt environment.