Testing Svelte Applications with Vitest

testingsveltevitestquality-assurance

Testing is a crucial aspect of delivering reliable Svelte applications. With Vitest, we have a powerful testing framework that integrates seamlessly with the Svelte ecosystem. Let’s explore how to implement effective testing strategies that ensure your applications remain robust and maintainable.

Why Vitest for Svelte Testing?

Vitest stands out as the optimal choice for testing Svelte applications for several reasons:

  • Native ESM support
  • SvelteKit integration out of the box
  • Exceptional performance through parallel test execution
  • Compatible with Jest’s expect syntax
  • Watch mode with smart file tracking

Setting Up Vitest in Your Svelte Project

The setup process is straightforward. If you’re using SvelteKit, Vitest is included by default. For other Svelte projects, you’ll need to install it:

pnpm add -D vitest @testing-library/svelte

Configure Vitest in your vite.config.ts:

import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';

export default defineConfig({
	plugins: [svelte({ hot: !process.env.VITEST })],
	test: {
		include: ['src/**/*.{test,spec}.{js,ts}'],
		environment: 'jsdom',
		globals: true,
	},
});

Writing Your First Test

Let’s start with a basic component test. Consider a simple counter component:

// src/lib/components/Counter.test.ts
import { describe, it, expect } from 'vitest';
import { render, fireEvent } from '@testing-library/svelte';
import Counter from './Counter.svelte';

describe('Counter', () => {
	it('increments value on button click', async () => {
		const { getByText } = render(Counter);

		const button = getByText('Increment');
		const value = getByText('0');

		await fireEvent.click(button);

		expect(getByText('1')).toBeTruthy();
	});
});

Testing Server Routes

For SvelteKit applications, testing server routes is crucial. Here’s how to test a typical server endpoint:

// src/routes/api/data/+server.test.ts
import { describe, it, expect } from 'vitest';
import { GET } from './+server';

describe('GET handler', () => {
	it('returns correct data structure', async () => {
		const response = await GET();
		const data = await response.json();

		expect(response.status).toBe(200);
		expect(data).toHaveProperty('items');
	});
});

Integration Testing Best Practices

When writing integration tests, focus on user interactions and business logic:

// src/routes/todos/+page.server.test.ts
import { describe, it, expect, beforeEach } from 'vitest';
import { POST } from './+server';

describe('Todo API', () => {
	beforeEach(() => {
		// Reset test database or state
	});

	it('creates new todo item', async () => {
		const formData = new FormData();
		formData.append('title', 'Test Todo');

		const response = await POST({
			request: new Request('', {
				method: 'POST',
				body: formData,
			}),
		});

		const data = await response.json();
		expect(data.title).toBe('Test Todo');
		expect(response.status).toBe(201);
	});
});

Testing Loading States

Testing loading states ensures a smooth user experience:

// src/routes/data/+page.test.ts
import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/svelte';
import Page from './+page.svelte';

describe('Data Page', () => {
	it('shows loading state', () => {
		render(Page, { props: { loading: true } });
		expect(screen.getByText('Loading...')).toBeTruthy();
	});
});

Mocking External Dependencies

Effective testing often requires mocking external dependencies:

// src/lib/api.test.ts
import { describe, it, expect, vi } from 'vitest';
import { fetch_data } from './api';

vi.mock('$app/environment', () => ({
	browser: true,
}));

describe('API', () => {
	it('handles network errors gracefully', async () => {
		vi.spyOn(global, 'fetch').mockRejectedValueOnce(
			new Error('Network error'),
		);

		const result = await fetch_data();
		expect(result.error).toBe('Failed to fetch data');
	});
});

Measuring Test Coverage

Vitest provides built-in coverage reporting. Add this to your vite.config.ts:

test: {
  coverage: {
    reporter: ['text', 'json', 'html'],
    exclude: ['node_modules/', 'src/**/*.{test,spec}.{js,ts}']
  }
}

Run coverage reports with:

pnpm vitest run --coverage

Conclusion

Implementing a robust testing strategy with Vitest in your Svelte applications leads to:

  • Reduced bugs in production
  • Confident code refactoring
  • Improved development velocity
  • Better documentation through tests
  • Enhanced team collaboration

By following these testing patterns, you’ll build more reliable applications while maintaining development efficiency. Remember, tests are not just about catching bugs—they’re about building confidence in your codebase and enabling faster, safer iterations.