git clone https://github.com/Intense-Visions/harness-engineering
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/claude-code/svelte-testing-patterns" ~/.claude/skills/intense-visions-harness-engineering-svelte-testing-patterns && rm -rf "$T"
agents/skills/claude-code/svelte-testing-patterns/SKILL.mdSvelte Testing Patterns
Test Svelte 5 components with Vitest and @testing-library/svelte — render, user events, store mocking, and async tick flushing
When to Use
- You are writing unit tests for Svelte components
- You need to test reactive behavior triggered by user interactions (clicks, input, form submission)
- You need to mock Svelte stores or module imports in tests
- You are testing async behavior and need to flush the update queue with
tick()
Instructions
Setup:
- Install dependencies and configure Vitest with the Svelte plugin:
npm install -D vitest @testing-library/svelte @testing-library/jest-dom jsdom
// vitest.config.ts import { defineConfig } from 'vitest/config'; import { svelte } from '@sveltejs/vite-plugin-svelte'; export default defineConfig({ plugins: [svelte({ hot: !process.env.VITEST })], test: { environment: 'jsdom', setupFiles: ['./src/tests/setup.ts'], globals: true, }, });
// src/tests/setup.ts import '@testing-library/jest-dom';
Rendering components:
- Use
fromrender
to mount components. Query the DOM with Testing Library queries:@testing-library/svelte
import { render, screen } from '@testing-library/svelte'; import MyButton from '$lib/components/MyButton.svelte'; test('renders with label', () => { render(MyButton, { props: { label: 'Click me' } }); expect(screen.getByRole('button', { name: 'Click me' })).toBeInTheDocument(); });
- Pass props using the
option:props
render(UserCard, { props: { user: { id: 1, name: 'Alice', email: 'alice@example.com' }, }, });
Firing events:
- Use
or thefireEvent
library for simulating interactions:userEvent
import { render, screen, fireEvent } from '@testing-library/svelte'; import Counter from '$lib/components/Counter.svelte'; test('increments on click', async () => { render(Counter); const button = screen.getByRole('button', { name: '+' }); await fireEvent.click(button); await fireEvent.click(button); expect(screen.getByText('2')).toBeInTheDocument(); });
- Use
for more realistic interactions (typing, tabbing):@testing-library/user-event
import userEvent from '@testing-library/user-event'; test('types in search input', async () => { const user = userEvent.setup(); render(SearchBar); await user.type(screen.getByRole('searchbox'), 'svelte'); expect(screen.getByDisplayValue('svelte')).toBeInTheDocument(); });
Async updates with tick:
- After triggering reactive updates, await
fromtick()
to flush Svelte's update queue:svelte
import { tick } from 'svelte'; test('shows error after failed submit', async () => { render(LoginForm); await fireEvent.submit(screen.getByRole('form')); await tick(); expect(screen.getByText('Email is required')).toBeInTheDocument(); });
Testing stores:
- Mock writable stores by importing and setting them directly:
import { count } from '$lib/stores/counter'; import CounterDisplay from '$lib/components/CounterDisplay.svelte'; test('displays current count', () => { count.set(42); render(CounterDisplay); expect(screen.getByText('42')).toBeInTheDocument(); });
Mocking modules:
- Use
to mock module imports in tests:vi.mock
vi.mock('$lib/services/api', () => ({ fetchUsers: vi.fn().mockResolvedValue([ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, ]), })); test('renders fetched users', async () => { render(UserList); await tick(); expect(screen.getByText('Alice')).toBeInTheDocument(); expect(screen.getByText('Bob')).toBeInTheDocument(); });
Testing with context:
- Wrap components in a context provider using a wrapper component, or use the
option:context
render(MyComponent, { context: new Map([['theme', { primary: '#3b82f6' }]]), });
Details
Svelte 5 vs. Svelte 4 testing differences:
Svelte 5 with
@testing-library/svelte v5+ uses the new component API internally. The test API (render, screen, fireEvent) is unchanged from the user's perspective. Ensure @testing-library/svelte v5 or later for Svelte 5 components.
The Testing Library philosophy:
Prefer queries that reflect how users perceive the UI:
— most semantic (button, heading, checkbox, etc.)getByRole
— for form fieldsgetByLabelText
— fallback for inputsgetByPlaceholderText
— visible text contentgetByText
— last resort (addgetByTestId
to elements)data-testid
Avoid testing implementation details (component state, store values) — test observable behavior.
in Svelte:act
Unlike React Testing Library, Svelte Testing Library uses
tick() for flushing updates rather than act(). Most interactions already resolve automatically with await; tick() is needed when updates are triggered by direct reactive changes rather than DOM events.
Testing SvelteKit load functions:
Load functions are plain async functions — test them directly without mounting a component:
import { load } from './+page.server'; test('returns user data', async () => { const result = await load({ params: { id: '1' }, locals: { user: mockUser }, fetch: global.fetch, } as any); expect(result.user.id).toBe(1); });
Snapshot testing:
test('matches snapshot', () => { const { container } = render(MyComponent); expect(container).toMatchSnapshot(); });
Use sparingly — prefer behavior assertions over structural snapshots.
Source
https://kit.svelte.dev/docs/testing
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.