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.
Nested Component Pattern (Recommended)
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/svelte | vitest-browser-svelte |
|---|---|---|
| Context | render(Comp, { context }) | Not supported |
| Solution | Direct context map | Wrapper component |
Wrapper Component Checklist
- Create wrapper component with
setContextcalls - 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