Context Testing

Overview

Testing Svelte components that use setContext/getContext requires a wrapper component pattern. Unlike @testing-library/svelte, vitest-browser-svelte does not support passing a context map directly to the render function.

This guide covers the wrapper component pattern for testing components that depend on Svelte context.

The Problem

When migrating from @testing-library/svelte, you may have tests like:

1// This pattern does NOT work in vitest-browser-svelte
2import { render } from '@testing-library/svelte';
3
4const context = new Map();
5context.set('apiClient', apiClient);
6context.set('authUserState', { user: { firstname: 'John' } });
7
8render(MyComponent, {
9	props: { children: callback },
10	context, // vitest-browser-svelte doesn't support this
11});

The render function from vitest-browser-svelte does not accept a context option. You need a different approach.

For testing a specific component with context, create a dedicated test wrapper. This is the recommended approach.

Test Wrapper for Specific Component

1<!-- user-profile.test-wrapper.svelte -->
2<script lang="ts">
3	import { setContext } from 'svelte';
4	import UserProfile from './user-profile.svelte';
5
6	interface Props {
7		api_client: unknown;
8		user_id?: string;
9	}
10
11	let { api_client, user_id = '1' }: Props = $props();
12
13	setContext('apiClient', api_client);
14</script>
15
16<UserProfile {user_id} />

Test File

1import { describe, expect, it, vi } from 'vitest';
2import { render } from 'vitest-browser-svelte';
3import { page } from 'vitest/browser';
4
5import UserProfileTestWrapper from './user-profile.test-wrapper.svelte';
6
7describe('UserProfile', () => {
8	const create_mock_api = (overrides = {}) => ({
9		get: vi.fn().mockResolvedValue({
10			id: '1',
11			email: '[email protected]',
12			firstname: 'John',
13			lastname: 'Doe',
14		}),
15		...overrides,
16	});
17
18	it('should display user information', async () => {
19		const mock_api = create_mock_api();
20
21		await render(UserProfileTestWrapper, {
22			api_client: mock_api,
23			user_id: '1',
24		});
25
26		await expect
27			.element(page.getByText('John Doe'))
28			.toBeInTheDocument();
29	});
30
31	it('should show loading state', async () => {
32		const mock_api = create_mock_api({
33			get: vi.fn().mockImplementation(
34				() => new Promise(() => {}), // Never resolves
35			),
36		});
37
38		await render(UserProfileTestWrapper, {
39			api_client: mock_api,
40		});
41
42		await expect
43			.element(page.getByTestId('loading'))
44			.toBeInTheDocument();
45	});
46
47	it('should handle API errors', async () => {
48		const mock_api = create_mock_api({
49			get: vi.fn().mockRejectedValue(new Error('Network error')),
50		});
51
52		await render(UserProfileTestWrapper, {
53			api_client: mock_api,
54		});
55
56		await expect
57			.element(page.getByText('Failed to load user'))
58			.toBeInTheDocument();
59	});
60});

Layout Context Pattern

For testing page components that depend on layout context:

Layout Test Wrapper

1<!-- test-utils/layout-wrapper.svelte -->
2<script lang="ts">
3	import { setContext } from 'svelte';
4	import type { Snippet } from 'svelte';
5
6	interface Props {
7		api_client: unknown;
8		auth_state?: { user: unknown; is_authenticated: boolean };
9		theme?: 'light' | 'dark';
10		children: Snippet;
11	}
12
13	let {
14		api_client,
15		auth_state = { user: null, is_authenticated: false },
16		theme = 'light',
17		children,
18	}: Props = $props();
19
20	setContext('apiClient', api_client);
21	setContext('authState', auth_state);
22	setContext('theme', theme);
23</script>
24
25<div class="layout-wrapper" data-theme={theme}>
26	{@render children()}
27</div>

Page Component Test

1import { describe, expect, it, vi } from 'vitest';
2import { render } from 'vitest-browser-svelte';
3import { page } from 'vitest/browser';
4
5import DashboardTestWrapper from './dashboard-page.test-wrapper.svelte';
6
7describe('Dashboard Page', () => {
8	it('should display authenticated user dashboard', async () => {
9		const mock_api = {
10			get: vi.fn().mockResolvedValue({ stats: [] }),
11		};
12		const mock_auth = {
13			user: { firstname: 'John', role: 'admin' },
14			is_authenticated: true,
15		};
16
17		await render(DashboardTestWrapper, {
18			api_client: mock_api,
19			auth_state: mock_auth,
20		});
21
22		await expect
23			.element(page.getByText('Welcome back, John'))
24			.toBeInTheDocument();
25	});
26});

Factory Function Pattern

Create reusable test utilities with factory functions:

1// test-utils/context-factories.ts
2import { vi } from 'vitest';
3
4export const create_mock_api_client = (overrides = {}) => ({
5	get: vi.fn().mockResolvedValue({}),
6	post: vi.fn().mockResolvedValue({}),
7	put: vi.fn().mockResolvedValue({}),
8	delete: vi.fn().mockResolvedValue({}),
9	...overrides,
10});
11
12export const create_mock_auth_state = (overrides = {}) => ({
13	user: null,
14	is_authenticated: false,
15	login: vi.fn(),
16	logout: vi.fn(),
17	...overrides,
18});

Quick Reference

Key Differences from @testing-library/svelte

Feature@testing-library/sveltevitest-browser-svelte
Contextrender(Comp, { context })Not supported
SolutionDirect context mapWrapper component

Wrapper Component Checklist

  • Create wrapper component with setContext calls
  • Accept context values as props
  • Use {@render children()} for child content
  • Create component-specific test wrappers when needed

Anti-Patterns to Avoid

  • Trying to pass context map to render function
  • Creating overly complex generic wrappers
  • Not mocking API clients properly
  • Forgetting to await async operations