React Testing Guide
Use this guide when writing or reviewing React tests that exercise FaasJS request flows in hooks or components.
Apply the shared Testing Guide first, then use the React-specific rules below.
For pure presentational components or hooks that do not touch FaasJS request flows, follow the shared Testing Guide and React Guide instead.
In this guide, "UI tests" means tests routed to the Vitest ui project with environment: 'jsdom'.
Applicable Scenarios
- Testing hooks that make FaasJS requests
- Testing components that trigger FaasJS requests
- Deciding how to use
setMockin request tests - Setting up shared mock cleanup in Vitest
- Choosing between hook tests and component tests for request-flow scenarios
Default Workflow
- Start with the shared Testing Guide.
- Name React hook and component UI tests with
.test.tsxwhen the file uses TSX, or.ui.test.tswhen the test does not use TSX syntax. - Clear the global mock in shared Vitest setup with
afterEach(() => setMock(null)). - Set the specific mock for each test or
beforeEach. - Prefer request-layer mocks such as
setMockover mocking local hooks, functions, or components. - Test observable behavior instead of implementation details.
- Cover loading, error, reload, skip, debounce, and controlled-props behavior when those flows exist.
- Use hook tests for hook behavior and component tests for visible UI behavior.
Rules
1. Use setMock instead of real network requests
- React unit tests that cover
@faasjs/reactrequest flows should usesetMock. - Do not rely on external services, real fetch timing, or environment-specific backends in unit tests.
- Keep mock setup explicit and local to the test scenario.
- When a test uses
@testing-library/react,renderHook,window, or other browser-like APIs, use.test.tsxfor TSX-based UI tests and.ui.test.tsfor non-TSX UI tests so Vitest can route them to theuiproject without relying on package location.
Vitest setup example:
import { afterEach } from 'vitest'
import { setMock } from '@faasjs/react'
afterEach(() => {
setMock(null)
})
2. Start with the smallest mock that fits the scenario
- Use a static object when one response is enough.
- Use a handler when the response depends on path or params.
- Use a streaming mock only when the code under test actually reads a stream.
- Prefer
stringToStreamas a helper for simple text-stream scenarios so the test setup stays compact.
Basic examples:
import { setMock } from '@faasjs/react'
setMock({
data: {
name: 'FaasJS',
},
})
setMock({
status: 500,
data: {
message: 'Internal Server Error',
},
})
Handler examples:
import { setMock } from '@faasjs/react'
setMock(async () => ({
data: {
name: 'FaasJS',
},
}))
setMock(async (path) => {
if (path === 'features/users/api/get') {
return {
data: {
id: 1,
name: 'FaasJS',
},
}
}
if (path === 'features/posts/api/get') {
return {
data: {
id: 2,
title: 'Hello',
},
}
}
return {
status: 404,
data: {
message: 'Not Found',
},
}
})
setMock(async (path, params) => {
if (path === 'features/users/api/get' && params.id === 1) {
return {
data: {
id: 1,
name: 'Admin',
},
}
}
if (path === 'features/users/api/get' && params.id === 2) {
return {
data: {
id: 2,
name: 'Editor',
},
}
}
return {
status: 404,
data: {
message: 'User not found',
},
}
})
Streaming example:
import { stringToStream } from '@faasjs/utils'
import { setMock } from '@faasjs/react'
setMock({
body: stringToStream('hello world'),
})
3. Keep mock boundaries at the request layer when possible
- Follow the shared Testing Guide rule against unnecessary mocks.
- When React request behavior can be exercised through
setMock, prefer that over mocking local hooks, components, or helpers. - Keep child components and local helpers real unless a clear external boundary forces isolation.
- Use
setMockto model the request contract, not to recreate component internals.
4. Add focused component tests for visible behavior
- Prefer
@testing-library/reactfor component tests. - Assert what users can see or trigger.
- Cover success and failure states when the component owns a request.
Example:
import { render, screen } from '@testing-library/react'
import { beforeEach, describe, expect, it } from 'vitest'
import { setMock, useFaas } from '@faasjs/react'
function UserName() {
const { data } = useFaas<{ name: string }>('features/users/api/get', {})
return <div>{data?.name}</div>
}
describe('UserName', () => {
beforeEach(() => {
setMock(async () => ({
data: {
name: 'FaasJS',
},
}))
})
it('renders mocked data', async () => {
render(<UserName />)
expect(await screen.findByText('FaasJS')).toBeDefined()
})
})
5. Add focused hook tests for hook behavior
- Prefer
renderHookwhen the behavior can be verified without rendering a full UI. - Cover behavior such as reload, skip, debounce, loading, error, and controlled props when relevant.
- When adding or changing mock-based request logic, include tests that prove the expected
setMockinteraction path.
Example:
import { renderHook, waitFor } from '@testing-library/react'
import { beforeEach, describe, expect, it } from 'vitest'
import { setMock, useFaas } from '@faasjs/react'
describe('useFaas', () => {
beforeEach(() => {
setMock(async (_path, params) => ({
data: params,
}))
})
it('supports reload with next params', async () => {
const { result } = renderHook(() =>
useFaas<{ id: number }>('features/users/api/get', { id: 1 }),
)
await waitFor(() => expect(result.current.data).toEqual({ id: 1 }))
await result.current.reload({ id: 2 })
await waitFor(() => expect(result.current.data).toEqual({ id: 2 }))
})
})
See Also
- Testing Guide — shared testing principles (apply first)
- React Data Fetching Guide — request flow patterns under test
- React Guide — component and hook patterns
Review Checklist
- shared Testing Guide rules are followed first
- UI tests use
.test.tsxwhen they contain TSX, or.ui.test.tswhen they do not - request-related tests use
setMockinstead of real network calls - shared Vitest setup clears mocks with
setMock(null) - mocks are no more complex than the scenario requires
- request tests keep mock boundaries at
setMockor another explicit external boundary when possible - components are tested through visible behavior
- hooks are tested through
renderHookwhen appropriate - tests cover relevant loading, error, reload, skip, debounce, or controlled-props flows