Unit testing
Learn how to write unit tests for your Nuxt integration.
The Nuxt SDK supports testing with Vitest and Nuxt Test Utils.
Here is what you need to check:
- Make sure your tracking calls are triggered correctly
- Verify you have defined fallbacks for your content
- Ensure you are handling evaluation errors
In the following sections, we will go through each of these points in detail.
Set up Vitest
Install the required dependencies:
npm install -D vitest @nuxt/test-utils @vue/test-utils happy-domCreate a vitest.config.ts in the root of your project:
import {defineVitestConfig} from '@nuxt/test-utils/config';
export default defineVitestConfig({ test: { globals: true, environment: 'nuxt', environmentOptions: { nuxt: { domEnvironment: 'happy-dom', }, }, },});Write tests
The Nuxt SDK fetches content through internal API routes. In tests, you can mock these routes and mount async components using the helpers from Nuxt Test Utils.
Content rendering
Consider a HomeHero component that fetches slot content with a fallback:
<script setup>const {data, error} = await useContent('home-hero', { fallback: { title: 'Default title', subtitle: 'Default subtitle', },});</script>
<template> <div v-if="data"> <h1>{{ data.content.title }}</h1> <p>{{ data.content.subtitle }}</p> </div> <div v-else-if="error"> <p>Something went wrong</p> </div></template>You can test both the success and error paths by mocking the content endpoint:
import {describe, it, expect} from 'vitest';import {mountSuspended, registerEndpoint} from '@nuxt/test-utils/runtime';import HomeHero from '../components/HomeHero.vue';
describe('HomeHero', () => { it('should render the personalized content', async () => { registerEndpoint('/api/_croct/content', { method: 'POST', handler: () => ({ content: { title: 'Personalized title', subtitle: 'Personalized subtitle', }, }), });
const wrapper = await mountSuspended(HomeHero);
expect(wrapper.find('h1').text()).toBe('Personalized title'); expect(wrapper.find('p').text()).toBe('Personalized subtitle'); });
it('should render the error state on failure', async () => { registerEndpoint('/api/_croct/content', { method: 'POST', handler: () => { throw createError({ statusCode: 500, statusMessage: 'Server error', }); }, });
const wrapper = await mountSuspended(HomeHero);
expect(wrapper.text()).toContain('Something went wrong'); });});Query evaluation
The same approach works for query evaluation. Consider a Greeting component:
<script setup>const {data: returning} = await useEvaluation("user is returning", { fallback: false,});</script>
<template> <h1 v-if="returning">Welcome back!</h1> <h1 v-else>Welcome!</h1></template>Mock the evaluation endpoint to control the query result:
import {describe, it, expect} from 'vitest';import {mountSuspended, registerEndpoint} from '@nuxt/test-utils/runtime';import Greeting from '../components/Greeting.vue';
describe('Greeting', () => { it('should greet returning users', async () => { registerEndpoint('/api/_croct/evaluate', { method: 'POST', handler: () => true, });
const wrapper = await mountSuspended(Greeting);
expect(wrapper.find('h1').text()).toBe('Welcome back!'); });
it('should use the fallback on failure', async () => { registerEndpoint('/api/_croct/evaluate', { method: 'POST', handler: () => { throw createError({ statusCode: 500, statusMessage: 'Server error', }); }, });
const wrapper = await mountSuspended(Greeting);
expect(wrapper.find('h1').text()).toBe('Welcome!'); });});Event tracking
Tracking is a client-side feature, so these tests use Vue Test Utils with the createCroct plugin from @croct/plug-nuxt/csr. This gives you direct access to the event listener API.
If you are using Vitest or any other test framework that sets NODE_ENV=test, the SDK auto-detects the test environment and uses a fake transport layer. You do not need to mock the network requests.
Consider a TrackButton component:
<script setup>import {useCroct} from '@croct/plug-nuxt/csr';
const croct = useCroct();
function onClick() { croct.track('goalCompleted', { goalId: 'ctaClicked', });}</script>
<template> <button @click="onClick">Click me</button></template>Mount the component with the Croct plugin and assert on the tracked events:
import {describe, it, expect, afterEach} from 'vitest';import {mount} from '@vue/test-utils';import {createCroct} from '@croct/plug-nuxt/csr';import croct from '@croct/plug';import TrackButton from '../components/TrackButton.vue';
describe('TrackButton', () => { afterEach(async () => { await croct.unplug(); });
it('should track a goal on click', async () => { const plugin = createCroct({ appId: '00000000-0000-0000-0000-000000000000', test: true, });
const wrapper = mount(TrackButton, { global: {plugins: [plugin]}, });
const listener = vi.fn(); croct.tracker.addListener(listener);
await wrapper.find('button').trigger('click');
expect(listener).toHaveBeenCalledWith( expect.objectContaining({ status: 'confirmed', event: { type: 'goalCompleted', goalId: 'ctaClicked', }, }), );
croct.tracker.removeListener(listener); });});You can find more details about the available events in the Event reference.