Unit tests are important to building robust applications. In this article explore test utils in Nuxt, what they do, different ways to opt in to a Nuxt testing environment in your unit tests and their accompanying helper functions.
Get notified when we release new tutorials, lessons, and other expert Nuxt content.
You probably already know that you need to write unit tests for your Nuxt app.
Thankfully, there’s an official library to help you do that — @nuxt/test-utils
.
In this article, we’ll cover:
Nuxt comes with a ton of magical features. It’s what makes the framework so good in the first place.
But you may have noticed that it makes writing tests more difficult.
If you try to test your components in Vitest like you would any other Vue app, those auto-imported components aren’t there. The middleware isn’t run when a route is loaded. The plugins are nowhere to be found. You also don’t have access to the Nuxt app instance, because no Nuxt app has been initialized!
These are the problems that the Nuxt test utils solve for us. It creates a whole Nuxt environment so that when we run our tests our code behaves as it would in a Nuxt app.
We actually have two different environments to choose from — one for unit testing, and one for end to end testing. We can only choose one per file because they conflict, but we’ll see how that works a little later.
Although you can run your unit tests with Jest, we’ll stick with using Vitest since that’s the most popular choice in the Vue community (it did come from the Vue community after all).
You’ll need to create a special Vitest config using a helper method provided by the library:
// vitest.config.ts
import { defineVitestConfig } from '@nuxt/test-utils/config';
export default defineVitestConfig({
// ...
});
But this doesn’t do much on its own. We need to tell Vitest which tests to create that Nuxt environment in. We can do this in a few ways.
The easiest way — and most legible, since you can see in your file explorer which files are using this context — is to do it in the filename with the special .nuxt.
suffix/prefix:
./tests/MyComponent.nuxt.test.ts
If you don’t want to do this, or can’t, you can also add a special comment at the top of your test file:
// @vitest-environment nuxt
You can also enable this unit testing environment globally for all tests in your Vitest config:
// vitest.config.ts
import { defineVitestConfig } from '@nuxt/test-utils/config';
export default defineVitestConfig({
test: {
environment: 'nuxt'
},
};
And then instead of opting in, you can opt out of this environment in each test that needs it:
// @vitest-environment node
There are a few other config options available if you need to further customize how this test Nuxt environment works.
Now let’s get to the juicy stuff — the helper methods that are going to really make our unit tests easy to write.
We’ve got a bunch, and I’ll show you an example of each in action:
mountSuspended
/ renderSuspended
mockNuxtImport
mockComponent
registerEndpoint
You’ll use this one when testing components. It allows you to mount components as they would be mounted in your Nuxt app.
This means it will auto-import all the things, run your async setup, initializes plugins, and so on:
import { describe, it, expect } from 'vitest';
import { mountSuspended } from '@nuxt/test-utils/runtime';
import MyComponent from './MyComponent.vue';
describe('MyComponent', () => {
it('renders the message correctly', async () => {
const wrapper = await mountSuspended(MyComponent);
expect(wrapper.text()).toContain('This component is set up.');
});
});
You’re also able to mount your app at a specific route, by passing in the App component and a route:
import { describe, it, expect } from 'vitest';
import { mountSuspended } from '@nuxt/test-utils/runtime';
import App from './App.vue';
describe('About', () => {
it('renders the about page', async () => {
const wrapper = await mountSuspended(App, { route: '/about' });
expect(wrapper.text()).toContain('Hi, my name is Michael!');
});
});
Using renderSuspended
instead of mountSuspended
allows you to use the @testing-library/vue
to write your tests instead. You’ll need to make sure to configure this in Vitest first, though.
This one makes it simpler to mock anything that gets auto-imported into your components, except for components! We have another method to mock components, which we’ll look at next.
Here’s an example of how you’d use it:
import { mockNuxtImport } from '@nuxt/test-utils/runtime';
mockNuxtImport('useAsyncData', () => {
return () => {
return { data: 'Mocked data' };
};
});
// ...tests
This lets you mock out any component using a factory function. It’s helpful because it allows you to shallow render a component, mocking out any descendent components to keep your tests simpler (and faster!).
You can use either the Options API to define the mock component inline, or define a separate file so you can use SFC and script setup
and all that good stuff you’re used to:
import { mockComponent } from '@nuxt/test-utils/runtime';
// Use Options API to configure
mockComponent('MyComponent', {
props: {
value: String
},
setup(props) {
// ...
},
});
// Or use a separate file to clean things up (and use <script setup>)
mockComponent('MyComponent', () => import('./MyComponent.mock.vue'));
// ...tests
Dealing with APIs in unit tests can be a real nightmare! This makes it super simple, by allowing you to mock out any of your endpoints:
import { registerEndpoint } from '@nuxt/test-utils/runtime';
import userTestData from './userTestData.json';
registerEndpoint('/users/', () => userTestData);
// ...tests
You can also mock external endpoints if you set the baseURL
of your Nuxt config to be empty when in the $test
environment override:
// nuxt.config.ts
export default defineNuxtConfig({
$test: {
app: {
baseURL: '',
},
},
$production: {
// ...
},
$development: {
// ...
}
})
This will force all URLs in your tests to go to your Nitro server so you can mock them out now as well.
The Nuxt Test Utils are fairly straightforward, but they provide a lot of value.
Here, we just saw how they can be used for writing unit tests, but we can also use them for end to end tests.
You can learn more about the test utils by reading the docs.