Can Remix route loader integration tests mock fetch without production credentials?
0 reputation · 18 Apr 2023, 14:29 UTC
0 reputation · 18 Apr 2023, 14:29 UTC
Goal: Verify that a Remix route loader integration test can replace live production credentials with mocked fetch or database calls while preserving server-side rendering behavior.
Constraint: Loaders execute on the server during the render phase and return serialized data to the client, so the test must account for server execution and client hydration without invoking real external services. Remix 2.0 changed loader execution order, so the target version may affect when mocks are invoked relative to rendering.
35775 reputation · 18 Apr 2023, 17:12 UTC
Yes, Remix route loader integration tests can mock fetch without production credentials by stubbing global.fetch before invoking the Remix request handler. This preserves server-side rendering behavior and enables hydration assertions.
Route loaders execute on the server during SSR and return serialized data to the browser. By intercepting fetch calls at the global scope before the request reaches Remix's handler, you can substitute any external service with a mock that returns the expected JSON structure.
global.fetch with a jest mock or similar stub prior to calling your Remix app's request handler.data- attributes, then hydrate in a test DOM to confirm client state matches.global.fetch in an afterEach hook to prevent cross-test contamination.Remix 2.0 introduced changes to loader execution timing, but the core strategy of mocking global.fetch before the request handler remains valid. Verify behavior with your specific Remix version, especially if using deferred loaders or parallel routes.
Compare the serialized loader data in the test response with the mocked payload. If they match, the loader correctly uses the mock. Then hydrate the response in a test environment (e.g., using React Testing Library or jsdom) and assert the client-side state reflects the mocked data.
If your loader uses json() with custom status or headers, confirm the test asserts on those response properties as well. Remix 2.0 may pass headers differently than earlier versions.
Use comments to ask for clarification. Post a solution as an answer.
35,775 reputation · 18 Apr 2023, 20:24 UTC
The critical detail often missed is when the mock needs to be installed relative to Remix's module evaluation. In Remix 2.0, loaders execute during SSR in module order, which means global fetch must be mocked before importing the route module. If you mock after require('./routes/blog'), the loader may have already captured the real fetch reference during module initialization.
Using Vitest, place mocks in setupFilesAfterEnv or use vi.stubGlobal('fetch', mockFetch) at the top of the test file before any app imports. This ensures the mock intercepts loader calls during the initial render pass, preserving the serialized data structure that useLoaderData() hydrates on the client.
Test the serialized output directly: capture what the loader returns after mocking and compare it to expected JSON structure. Then verify hydration by checking useLoaderData() returns identical data post-render. This two-phase check confirms both server serialization and client hydration work correctly without production credentials.