FaasJS
Home
  • Guide
  • Documents
  • Templates
  • Changelog
  • Ecosystem

    • Docker Images
  • Github
  • Contributing
  • Sponsor
  • Security
Home
  • Guide
  • Documents
  • Templates
  • Changelog
  • Ecosystem

    • Docker Images
  • Github
  • Contributing
  • Sponsor
  • Security
  • Curated Stack Guide
  • Application Slices Guide
  • Project Config Guide
  • File Conventions
  • Code Comments Guide
  • defineApi Guide
  • Jobs Guide
  • Testing Guide
  • React Guide
  • React Data Fetching Guide
  • React Testing Guide
  • Ant Design Guide
  • Node Utils Guide
  • Logger Guide
  • Utils Guide
  • PG Query Builder and Raw SQL Guide
  • PG Table Types Guide
  • PG Schema and Migrations Guide
  • PG Testing Guide
  • CLI and Tooling Guide
  • CRUD Patterns Guide
  • faas.yaml Configuration Specification
  • Getting Started Guide
  • HTTP Plugin Guide
  • HTTP Protocol Specification
  • JSON Guide
  • Middleware Guide
  • Naming Convention
  • Plugin Specification
  • Plugins Guide
  • Routing Mapping Specification
  • Validation Guide
  • YAML Guide

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 setMock in request tests
  • Setting up shared mock cleanup in Vitest
  • Choosing between hook tests and component tests for request-flow scenarios

Default Workflow

  1. Start with the shared Testing Guide.
  2. Name React hook and component UI tests with .test.tsx when the file uses TSX, or .ui.test.ts when the test does not use TSX syntax.
  3. Clear the global mock in shared Vitest setup with afterEach(() => setMock(null)).
  4. Set the specific mock for each test or beforeEach.
  5. Prefer request-layer mocks such as setMock over mocking local hooks, functions, or components.
  6. Test observable behavior instead of implementation details.
  7. Cover loading, error, reload, skip, debounce, and controlled-props behavior when those flows exist.
  8. 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/react request flows should use setMock.
  • 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.tsx for TSX-based UI tests and .ui.test.ts for non-TSX UI tests so Vitest can route them to the ui project 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 stringToStream as 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 setMock to model the request contract, not to recreate component internals.

4. Add focused component tests for visible behavior

  • Prefer @testing-library/react for 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 renderHook when 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 setMock interaction 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.tsx when they contain TSX, or .ui.test.ts when they do not
  • request-related tests use setMock instead 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 setMock or another explicit external boundary when possible
  • components are tested through visible behavior
  • hooks are tested through renderHook when appropriate
  • tests cover relevant loading, error, reload, skip, debounce, or controlled-props flows