Test Component Svelte
Test Svelte components with Testing Library using render, fireEvent, and waitFor
When to Use
- Testing Svelte component rendering and user interactions
- Verifying reactive state updates in response to events
- Testing components that use Svelte stores or slots
- Writing accessible, user-centric component tests
Instructions
- Setup — install dependencies:
npm install -D @testing-library/svelte @testing-library/jest-dom vitest jsdom
Configure Vitest for browser environment:
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import { svelte } from '@sveltejs/vite-plugin-svelte';
export default defineConfig({
plugins: [svelte({ hot: false })],
test: { environment: 'jsdom' },
});
- Render and query:
import { render, screen } from '@testing-library/svelte';
import Greeting from './Greeting.svelte';
it('displays the name', () => {
render(Greeting, { props: { name: 'Alice' } });
expect(screen.getByText('Hello, Alice!')).toBeInTheDocument();
});
- Simulate user interactions:
import { render, screen, fireEvent } from '@testing-library/svelte';
import Counter from './Counter.svelte';
it('increments count on button click', async () => {
render(Counter);
const button = screen.getByRole('button', { name: 'Increment' });
expect(screen.getByText('Count: 0')).toBeInTheDocument();
await fireEvent.click(button);
expect(screen.getByText('Count: 1')).toBeInTheDocument();
});
- Test form inputs:
import { render, screen, fireEvent } from '@testing-library/svelte';
import SearchBox from './SearchBox.svelte';
it('updates search results on input', async () => {
render(SearchBox);
const input = screen.getByPlaceholderText('Search...');
await fireEvent.input(input, { target: { value: 'hello' } });
expect(await screen.findByText('Results for: hello')).toBeInTheDocument();
});
- Test with props updates:
import { render, screen } from '@testing-library/svelte';
import UserCard from './UserCard.svelte';
it('updates when props change', async () => {
const { rerender } = render(UserCard, { props: { name: 'Alice' } });
expect(screen.getByText('Alice')).toBeInTheDocument();
await rerender({ name: 'Bob' });
expect(screen.getByText('Bob')).toBeInTheDocument();
});
- Test component events:
import { render, screen, fireEvent } from '@testing-library/svelte';
import Button from './Button.svelte';
it('dispatches click event with payload', async () => {
const { component } = render(Button);
const handler = vi.fn();
component.$on('customClick', handler);
await fireEvent.click(screen.getByRole('button'));
expect(handler).toHaveBeenCalledWith(expect.objectContaining({ detail: { action: 'confirm' } }));
});
- Test async/loading states:
it('shows loading then data', async () => {
render(UserList);
expect(screen.getByText('Loading...')).toBeInTheDocument();
expect(await screen.findByText('Alice')).toBeInTheDocument();
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
});
- Test with Svelte stores:
import { writable } from 'svelte/store';
it('reacts to store changes', async () => {
const user = writable({ name: 'Alice' });
render(UserDisplay, { props: { user } });
expect(screen.getByText('Alice')).toBeInTheDocument();
user.set({ name: 'Bob' });
await screen.findByText('Bob');
});
Details
@testing-library/svelte follows the same user-centric testing philosophy as the React variant. Tests interact with rendered DOM elements, not component internals.
Svelte-specific considerations:
- Svelte components are compiled — the test setup needs the Svelte Vite plugin to process
.sveltefiles - Reactivity is batched — after
fireEvent, changes may not be reflected immediately. Useawaitortick()fromsvelteto wait for updates - Component events use
$on('eventName', handler)— different from React's prop-based callbacks - Slot testing requires wrapper components that render slots with test content
Query priority (same as React Testing Library):
getByRole— accessible role and namegetByLabelText— form labelsgetByText— visible textgetByTestId— last resort
Svelte 5 runes: If using Svelte 5 with runes ($state, $derived), the testing patterns remain the same at the component level — Testing Library interacts with the DOM, which is the output of runes.
Trade-offs:
- Testing Library for Svelte has a smaller ecosystem than React Testing Library
fireEventin Svelte requiresawaitfor reactivity updates, which is easy to forget- Component event testing with
$onis less ergonomic than React's callback props - Svelte's compilation step means test startup is slightly slower than plain JavaScript tests
Source
https://testing-library.com/docs/svelte-testing-library/intro/
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.