Testing pages with layout dependencies.
Closed this issue · 1 comments
Is there a best practice or a good way to test code that has setup in a parent layout. For example, I used svelte kit latest sample and then tested a page that was using svelte query provider defined in the parent layout.
import { render, screen } from '@testing-library/svelte';
import '@testing-library/jest-dom/vitest';
import { describe, expect, test } from 'vitest';
import Page from './+page.svelte';
describe('/+page.svelte', () => {
test('should render h1', () => {
render(Page);
expect(screen.getByRole('heading', { level: 1 })).toBeInTheDocument();
});
});
This fails with:
FAIL client src/routes/status/page.svelte.test.ts > /+page.svelte > should render h1
Error: No QueryClient was found in Svelte context. Did you forget to wrap your component with QueryClientProvider?
❯ getQueryClientContext node_modules/@tanstack/svelte-query/dist/context.js:7:15
5| const client = getContext(_contextKey);
6| if (!client) {
7| throw new Error('No QueryClient was found in Svelte context. Did you forget to wrap your component with QueryClientProvider?');
| ^
8| }
I'm also noticing other things like dynamic env's are causing issues too.
@niemyjski this is a very good question! Thanks for taking the time to post it.
I lead a team maintaining a large SvelteKit app that uses svelte-query for its client-side API state management. This is a recommendation from me personally rather than anything "official," but I've found the best1 strategy for component tests is pretty classic Uncle Bob: separate the stuff you want to test away from the framework glue. In practice, this means:
- No component-level tests for
+page.svelte/+layout.svelte - No component-level tests for anything that wires into
createQuery,createMutation, etc. - No/few component-level tests for components that subscribe to context
- Sometimes, especially if its a context that you wrote, you can inject a context into the test instead of avoiding the test entirely
- Structure these "framework" components so they delegate all interesting work to props-driven components and/or pure functions, and write tests for those units instead of the glue
- Include small Playwright E2E suites to validate proper wire-up of your framework glue
- TypeScript and static analysis are also a great, if incomplete, tools to validate framework wire-up
For a simple, if slightly contrived example, if I wanted to test a user dashboard page's heading, I might write something like:
<!-- dashboard/+page.svelte -->
<script>
import { createUserQuery } from '$lib/api'
import DashboardHeading from './dashboard-heading.svelte'
const userQuery = createUserQuery()
</script>
<DashboardHeading user={$userQuery.data} /><!-- dashboard/dashboard-heading.svelte -->
<script>
import type { User } from '$lib/api';
const { user }: { user: User | undefined } = $props();
</script>
<h1>Hello {user?.name ?? 'friend'}!</h1>// dashboard-heading.spec.ts
// ...
it('renders level 1 heading with user name', () => {
render(DashboardHeading, { user: { name: 'Alice' } })
const heading = screen.getByRole('heading', { level: 1, name: /hello alice/iu })
expect(heading).toBeInTheDocument()
})From there, I would also make sure that I had an E2E test for this page that also checked that there was a level-1 heading on the page matching.
I've seen a number of guides online that recommend mocking out SvelteKit imports in your tests, but I definitely recommend your try to minimize or eliminate mocking in component tests. I'm a huge fan of mocking, but I've gone down the mock-heavy-component-tests path with React and RTL, and all it got me was a very brittle test suite that slowed my team down. Instead, I prefer to acknowledge that component tests are, by nature, highly integrated with code outside my control, and then do my best to minimize how many layers get tested in a given component test.
Footnotes
-
"Best" meaning "high test coverage of interesting, product-specific logic while prioritizing development speed." ↩