Harness-engineering svelte-testing-patterns

Svelte Testing Patterns

install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
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"
manifest: agents/skills/claude-code/svelte-testing-patterns/SKILL.md
source content

Svelte 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:

  1. 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:

  1. Use
    render
    from
    @testing-library/svelte
    to mount components. Query the DOM with Testing Library queries:
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();
});
  1. Pass props using the
    props
    option:
render(UserCard, {
  props: {
    user: { id: 1, name: 'Alice', email: 'alice@example.com' },
  },
});

Firing events:

  1. Use
    fireEvent
    or the
    userEvent
    library for simulating interactions:
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();
});
  1. Use
    @testing-library/user-event
    for more realistic interactions (typing, tabbing):
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:

  1. After triggering reactive updates, await
    tick()
    from
    svelte
    to flush Svelte's update queue:
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:

  1. 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:

  1. Use
    vi.mock
    to mock module imports in tests:
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:

  1. Wrap components in a context provider using a wrapper component, or use the
    context
    option:
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:

  1. getByRole
    — most semantic (button, heading, checkbox, etc.)
  2. getByLabelText
    — for form fields
  3. getByPlaceholderText
    — fallback for inputs
  4. getByText
    — visible text content
  5. getByTestId
    — last resort (add
    data-testid
    to elements)

Avoid testing implementation details (component state, store values) — test observable behavior.

act
in Svelte:

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

  1. Read the instructions and examples in this document.
  2. Apply the patterns to your implementation, adapting to your specific context.
  3. 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.