8 min read
Building Type-Safe APIs with Nuxt 3 and Prisma
Learn how to build end-to-end type-safe APIs using Nuxt 3 server routes and Prisma ORM for a seamless developer experience.
1.9K
Stop writing brittle tests. Learn a pragmatic testing strategy for Vue components using Vitest and Vue Test Utils.
Mbeah Essilfie
July 1, 2026 at 06:03 AM
Don't test implementation details. Test behavior — what the user sees and does.
pnpm add -D vitest @vue/test-utils happy-dom
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
test: {
environment: 'happy-dom',
globals: true,
},
})
<!-- components/AppButton.vue -->
<template>
<button
:disabled="loading"
:class="{ 'opacity-50': loading }"
@click="$emit('click')"
>
<span v-if="loading">Loading...</span>
<slot v-else />
</button>
</template>
import { mount } from '@vue/test-utils'
import AppButton from './AppButton.vue'
describe('AppButton', () => {
it('renders slot content', () => {
const wrapper = mount(AppButton, {
slots: { default: 'Click me' },
})
expect(wrapper.text()).toBe('Click me')
})
it('shows loading state', () => {
const wrapper = mount(AppButton, {
props: { loading: true },
})
expect(wrapper.text()).toBe('Loading...')
expect(wrapper.attributes('disabled')).toBeDefined()
})
it('emits click event', async () => {
const wrapper = mount(AppButton, {
slots: { default: 'Click' },
})
await wrapper.trigger('click')
expect(wrapper.emitted('click')).toHaveLength(1)
})
})
import { useCounter } from './useCounter'
describe('useCounter', () => {
it('increments count', () => {
const { count, increment } = useCounter()
expect(count.value).toBe(0)
increment()
expect(count.value).toBe(1)
})
})
import { flushPromises, mount } from '@vue/test-utils'
import UserProfile from './UserProfile.vue'
// Mock the fetch
vi.mock('~/composables/useApi', () => ({
useApi: () => ({
data: ref({ name: 'Mbeah', email: 'mbeah@test.com' }),
loading: ref(false),
error: ref(null),
}),
}))
describe('UserProfile', () => {
it('displays user data', async () => {
const wrapper = mount(UserProfile)
await flushPromises()
expect(wrapper.text()).toContain('Mbeah')
})
})
Test the contract: props in, events and rendered output out.
Fullstack Software Developer
Learn how to build end-to-end type-safe APIs using Nuxt 3 server routes and Prisma ORM for a seamless developer experience.
Go beyond basic utility classes and learn how to build cohesive, maintainable design systems with Tailwind CSS.
Understand the power of Vue 3's Composition API through practical, real-world composable patterns that you can use today.
