Svelte projects feel trustworthy only when the UI is tested automatically. Here is how to prove that components and server loads behave as expected.
Key terms
- Unit test: Automation that isolates a single component or function and checks that the rendered output or return value matches expectations.
- Integration test: A scenario-style test that verifies multiple components, stores, or load functions still work when combined.
- Vitest: A test runner optimized for Vite projects, which keeps feedback loops fast.
- Svelte Testing Library: A helper set that queries and interacts with the DOM the way an end user would.
Core ideas
Unit tests focus on tiny pieces of UI in isolation, while integration tests check flows that span components or even server load functions. Vitest is the runner that executes both types inside a Vite-native environment. Svelte Testing Library exposes DOM queries, so assertions describe what a user sees instead of specific implementation details.
Code examples
Walk through the setup, a rendering test, an interaction test, and a load function test.
// vitest.config.ts
export default defineConfig({
plugins: [svelte()],
test: {
environment: 'jsdom'
}
});
test('the card shows its title and assignee', () => {
const task = { title: 'Static analysis', assignee: 'Yuna', done: false };
const { getByText } = render(TaskCard, { props: { task } });
expect(getByText('Static analysis')).toBeInTheDocument();
expect(getByText('Yuna')).toBeInTheDocument();
});
test('clicking the complete button fires a toggle event', async () => {
const task = { title: 'Write docs', done: false };
const { component, getByRole } = render(TaskCard, { props: { task } });
const spy = vi.fn();
component.$on('toggle', spy);
await fireEvent.click(getByRole('button', { name: /complete/i }));
expect(spy).toHaveBeenCalledTimes(1);
});
test('load fetches subjects and tasks in parallel', async () => {
const fetch = vi.fn()
.mockResolvedValueOnce({ json: () => Promise.resolve([]) })
.mockResolvedValueOnce({ json: () => Promise.resolve([]) });
const data = await load({ fetch, parent: () => ({}) });
expect(fetch).toHaveBeenCalledTimes(2);
expect(data).toEqual({ subjects: [], tasks: [] });
});
These checks keep both the UI and server logic stable as the project evolves.
Output checks
With tests, the focus is on the resulting output more than the code itself.
:::terminal{title="Vitest run sample", showFinalPrompt="false"}
[
{ "cmd": "pnpm vitest run", "output": "✓ src/lib/components/TaskCard.test.ts (2)\n✓ src/lib/components/TaskForm.test.ts (1)\n✓ src/routes/dashboard/+page.server.test.ts (1)\n\nTest Files 3 passed\nTests 4 passed\nDuration 1.12s", "delay": 500 }
]
:::
- Confirm the summary clearly lists which files passed.
- Confirm component and
loadtests execute in the same run. - Confirm failure output points you straight to the file you should inspect first.
Why it matters
Small teams rarely have time to manually re-check every screen. Once tests are in place, you can refactor or upgrade dependencies with confidence that core flows remain intact.
Practice
- Try it: configure
vitest.config.tsand write the TaskCard render test. - Extend it: add the interaction test plus the
loadtest to cover async flows. - Debug it: when you see a failure message, double-check that the test data matches the component props.
- Definition of done: at least three tests pass when you run
pnpm test.
Wrap-up
Test automation becomes the safety net for future real-time features and during deployment. Next up is learning how to keep data in sync with real-time update patterns.
💬 댓글
이 글에 대한 의견을 남겨주세요