🔥 (#164) Unit Testing in Nuxt

Hey all!

I've got some great tips for you this week on unit testing in Nuxt.

In case you missed it, the latest episode of Deja Vue was on Nuxt server components with Julien Huang, another member of the Nuxt team.

The episode covers a whole bunch of interesting topics, including:

  • How Julien only started programming a few years ago before joining the Nuxt team
  • Nuxt Server Components, a focus of Julien's
  • How server components are handled on the client side
  • What we can expect in the future from server components

and so much more!

Find the episode on your favourite platform here.

If you have any suggestions for topics or guests you'd like to hear from, let me know! Just reply to this email.

Now, on to the tips!

— Michael

🔥 Mock Any Import in Nuxt

One handy helper method in @nuxt/test-utils is mockNuxtImport.

It's a convenience method to make it easier to mock anything that Nuxt would normally auto-import:

import { mockNuxtImport } from '@nuxt/test-utils/runtime';
mockNuxtImport('useAsyncData', () => {
return () => {
return { data: 'Mocked data' };
};
});
// ...tests

🔥 Easy Unit Testing in Nuxt

For your unit tests, @nuxt/test-utils lets you opt-in to a Nuxt environment by adding .nuxt. to the filename of your test:

./tests/MyComponent.nuxt.test.ts

You can also add a special comment at the top of the file:

@vitest-environment nuxt

Or enable the environment for all Vitest tests in your config:

// vitest.config.ts
import { defineVitestConfig } from '@nuxt/test-utils/config';
export default defineVitestConfig({
test: {
environment: 'nuxt'
},
};

🔥 Mount Components When Testing in Nuxt

When writing unit tests, you have access to a bunch of helper methods.

One super useful one is mountSuspended. It lets you mount any component inside your Nuxt context with async setup:

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!');
});
});

📜 Helpful Patterns I Use in Vue

Although I disagree with using custom elements to get a flatter component hierarchy, Brennan has a bunch of useful tips and patterns in this article.

I use the loading state one all the time.

Check it out here: Helpful Patterns I Use in Vue

đź’¬ The Code You Don't Write

"The code you write makes you a programmer. The code you delete makes you a good one. The code you don't have to write makes you a great one." — Mario Fusco

đź§  Spaced-repetition: Writable Computed Refs

The best way to commit something to long-term memory is to periodically review it, gradually increasing the time between reviews 👨‍🔬

Actually remembering these tips is much more useful than just a quick distraction, so here's a tip from a couple weeks ago to jog your memory.

Computed refs are cool and all, but did you know you can create writable computed refs?

const firstName = ref('');
const lastName = ref('');
const fullName = computed({
get: () => `${firstName.value} ${lastName.value}`,
set: (val) => {
const split = val.split(' '); // ['Michael', 'Thiessen']
firstName.value = split[0]; // 'Michael'
lastName.value = split[1]; // 'Thiessen'
}
});
fullName.value = 'Michael Thiessen';
console.log(lastName.value); // 'Thiessen'



p.s. I also have four products/courses: Clean Components Toolkit, Vue Tips Collection 2, Mastering Nuxt 3, and Reusable Components