Cleanest way to mock Croct hooks in Vitest so component tests stay offline
hi, trying to keep our component tests fully offline. Minimal repro:
// Hero.tsximport {useContent} from '@croct/plug-react';
export function Hero() { const {title} = useContent('home-hero');
return <h1>{title}</h1>;}In jsdom the hook never resolves, so render(<Hero />) inside a Suspense boundary just sits on the fallback forever and findByText times out. What is the standard way people mock this in Vitest? I would rather not rewrite the component to take content as a prop just for tests.
2 answers
For unit tests, mock the module. vi.mock makes useContent return fixture content synchronously, so the Suspense path never kicks in and the test is deterministic and offline:
import {render, screen} from '@testing-library/react';import {vi} from 'vitest';import {Hero} from './Hero';
vi.mock('@croct/plug-react', () => ({ useContent: () => ({title: 'Fixture title'}),}));
test('renders the hero title', () => { render(<Hero />);
expect(screen.getByText('Fixture title')).toBeInTheDocument();});No provider, no Suspense boundary, no network. If different tests need different content, swap the factory for vi.fn() and set the return value per test.
This is the right altitude for component tests: you are testing your rendering logic, not the SDK's fetching behavior.
perfect, the per-test vi.fn() variant is what we ended up with. all green and zero network calls, thanks!
Adding the option for tests that keep the real provider (integration-style runs):
- Pass an initial or fallback value to useContent in the component. The hook then renders immediately with that value instead of suspending, which also makes jsdom happy.
- The plug options include a test mode flag (default false) that swaps in a mock transport, so even a mounted provider sends nothing over the wire.
We use module mocks for unit tests like Felix showed, and the test flag for the handful of tests that exercise the provider wiring itself. Both have their place.