Unit Testing in Nuxt

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.

Michael Thiessen
Nuxt 3

Mastering Nuxt 3 course is here!

Get notified when we release new tutorials, lessons, and other expert Nuxt content.

Click here to view the Nuxt 3 course

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:

  • What the test utils do
  • Different ways to opt in to a Nuxt testing environment in your unit tests
  • The different helper methods that come with it

What do the test utils do for us?

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.

Setting up our Nuxt test environment

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.

  1. Suffix in the filename
  2. Special comment at the top of the file
  3. Globally in our config

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.

Unit Testing Helper Methods

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

mountSuspended / renderSuspended

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.

mockNuxtImport

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

mockComponent

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

registerEndpoint

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.

Making Unit Tests Easier

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.

Michael Thiessen
Michael is a passionate full time Vue.js and Nuxt.js educator. His weekly newsletter is sent to over 11,000 Vue developers, and he has written over a hundred articles for his blog and VueSchool.

Follow MasteringNuxt on