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:
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
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
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.tsimport { defineVitestConfig } from '@nuxt/test-utils/config';export default defineVitestConfig({test: {environment: '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!');});});
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 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
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