v4.0.0
4.0.0 is the next major release.
π Highlights
We're releasing Nuxt Test Utils v4, with support for Vitest v4. π
Better mocking support
The biggest improvement in this release is how mocking works. Nuxt initialization has been moved from setupFiles to the beforeAll hook (#1516), which means vi.mock and mockNuxtImport calls now take effect before Nuxt starts. This fixes a long-standing issue where mocks for composables used in middleware or plugins wouldn't apply reliably (#750, #836, #1496).
On top of that, mockNuxtImport now passes the original implementation to the factory function (#1552), making partial mocking much more natural:
mockNuxtImport('useRoute', original => vi.fn(original))
it('my test', async () => {
vi.mocked(useRoute).mockImplementation(
(...args) => ({ ...vi.mocked(useRoute).getMockImplementation()!(...args), path: '/mocked' }),
)
const wrapper = await mountSuspended(MyComponent)
expect(wrapper.find('#path').text()).toBe('/mocked')
})registerEndpoint improvements
registerEndpoint now works correctly with query parameters in URLs (#1560), and endpoints registered in setup files are no longer lost when modules reset (#1549).
π§ Migration
@nuxt/test-utils v4 contains a few breaking changes, almost all related to requiring at least vitest v4 as a peer dependency (if you are using vitest). It replaces vite-node with Vite's native Module Runner and simplifies pool configuration.
This will mean improvements for test performance and mocking, but does require some changes to your test code.
Tip
Most of the changes below are straightforward. The biggest thing to watch out for is code that runs at the top level of a describe block β see below.
Update your dependencies
Update vitest and its companion packages together:
{
"devDependencies": {
- "@nuxt/test-utils": "^3.x",
- "vitest": "^3.x",
- "@vitest/coverage-v8": "^3.x"
+ "@nuxt/test-utils": "^4.0",
+ "vitest": "^4.0",
+ "@vitest/coverage-v8": "^4.0"
}
}Peer dependencies
We've tightened peer dependency ranges. You may need to update some of these:
| Dependency | v3 | v4 |
|---|---|---|
vitest |
^3.2.0 |
^4.0.2 |
happy-dom |
* |
>=20.0.11 |
jsdom |
* |
>=27.4.0 |
@jest/globals |
^29.5.0 || >=30.0.0 |
>=30.0.0 |
@cucumber/cucumber |
^10.3.1 || >=11.0.0 |
>=11.0.0 |
@testing-library/vue |
^7.0.0 || ^8.0.1 |
^8.0.1 |
Later environment setup
This is the change that might require most change in your tests. Because we've moved the nuxt environment setup into beforeAll, this means composables called at the top level of a describe block will fail with [nuxt] instance unavailable.
// Before (worked in vitest v3)
describe('my test', () => {
const router = useRouter() // ran lazily, after environment setup
// ...
})
// After (vitest v4)
describe('my test', () => {
let router: ReturnType<typeof useRouter>
beforeAll(() => {
router = useRouter() // runs after environment setup
})
// ...
})This applies to useRouter(), useNuxtApp(), useRoute(), and any other Nuxt composable or auto-import.
Tip
If you only need the value within individual tests, beforeEach or directly within the test works too.
Stricter mock exports
If you use vi.mock with a factory function, accessing an export that the factory doesn't return will now throw an error instead of silently returning undefined.
// Before: accessing `bar` would silently return undefined
vi.mock('./module', () => ({ foo: 'mocked' }))
// After: accessing `bar` throws
// Fix: use importOriginal to include all exports
vi.mock('./module', async (importOriginal) => ({
...await importOriginal(),
foo: 'mocked',
}))Note
If you're mocking a virtual module (like #build/nuxt.config.mjs) where importOriginal can't resolve the real module, you might need to explicitly list all accessed exports in your mock factory.
Other changes
For the full list, see the vitest v4 migration guide.
π Changelog
π Enhancements
- deps:
β οΈ Upgrade to vitest v4 (#1481) - deps:
β οΈ Drop official support for older versions of test runners + dom environments (31fdc262a) - runtime-utils: Pass original to
mockNuxtImportfactory (#1552) - runtime:
β οΈ Change nuxt start timing tobeforeAllhook (#1516) - e2e: Support setup and teardown timeouts in setupBun (#1578)
π©Ή Fixes
- runtime: Handle optional chaining vueWrapper plugin installed check (#1547)
- runtime-utils: Keep endpoints from registerEndpoint in setup file (#1549)
- runtime-utils: Support
registerEndpointwith query params (#1560) - runtime-utils: Avoid local variable in mockNuxtImport macro (#1564)
- runtime-utils: Add missing
nextTickimport (#1563) - Pin h3-next to patch (1ff3bbb91)
- playwright: Bump windows timeout (63e39b7c9)
- config: Respect include options in non nuxt environment simple setup (#1570)
π Refactors
- module: Use
@nuxt/devtools-kitfor devtools hooks (426e0b537) - runtime: Remove unnecessary querySelector (#1577)
π Documentation
- Add nuxt.care health badge (1499a48de)
π‘ Chore
- Allow changelog update util to return major bump (9e86cadab)
- Make
app-vitestfollow advised setup guidelines (#1542) - Update lockfile (6d798b5e1)
- config: Migrate Renovate config (#1568)
- Add test utils setup to
.nuxtrc(b4021dee4)
β Tests
- Avoid running root test script twice (44f6bd396)
β οΈ Breaking Changes
- deps:
β οΈ Upgrade to vitest v4 (#1481) - deps:
β οΈ Drop official support for older versions of test runners + dom environments (31fdc262a) - runtime:
β οΈ Change nuxt start timing tobeforeAllhook (#1516)
β€οΈ Contributors
- Yoshihiro Yamaguchi (@yamachi4416)
- Daniel Roe (@danielroe)
- Adam DeHaven (@adamdehaven)