Testing Patterns (@data-client/test)

SkillDev tools

Lets your agent write and debug Jest tests for React hooks and components using the Data Client library.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Testing Patterns (@data-client/test) skill

About this capability

Test @data-client/react with renderDataHook and mountDataClient - jest unit tests, fixtures, interceptors, MockResolver, mock responses, nock HTTP mocking, fake timers for polling/subscription tests, DataProvider test setup, hook and component testing. Use when writing or debugging tests for hooks,

What this skill tells your AI

The instructions your AI receives, as published by reactive/data-client in .agents/skills/data-client-react-testing/SKILL.md and read by ahel’s review.

Hook Testing with renderDataHook()

import { renderDataHook } from '@data-client/test';

it('useSuspense() should render the response', async () => {
  const { result, waitFor } = renderDataHook(
    () => useSuspense(ArticleResource.get, { id: 5 }),
    {
      initialFixtures: [
        {
          endpoint: ArticleResource.get,
          args: [{ id: 5 }],
          response: { id: 5, title: 'hi ho', content: 'whatever' },
        },
      ],
    },
  );
  expect(result.current.title).toBe('hi ho');
});

Options:

  • initialFixtures - Set up initial state of the store
  • resolverFixtures - Add interceptors for subsequent requests
  • getInitialInterceptorData - Simulate changing server state

Return values:

  • Inherits all renderHook() return values from @testing-library/react
  • controller - Controller instance for manual actions
  • allSettled() - Wait for all async operations to complete

Cleanup is automatic -- an afterEach hook is registered at module load time that drains all active cleanups. No manual renderDataHook.cleanup() calls are needed.

Fixtures and Interceptors

Success Fixture:

interface SuccessFixture {
  endpoint;
  args;
  response;
  error?;
  delay?;
}

Response Interceptor:

interface ResponseInterceptor {
  endpoint;
  response(...args);
  delay?;
  delayCollapse?;
}

Testing Mutations

Create operations:

it('should create a new todo', async () => {
  const { result } = renderDataHook(
    () => useController(),
    {
      initialFixtures: [
        {
          endpoint: TodoResource.getList,
          args: [],
          response: [],
        },
      ],
      resolverFixtures: [
        {
          endpoint: TodoResource.getList.push,
          response: (newTodo) => ({ ...newTodo, id: 1 }),
        },
      ],
    },
  );

  const newTodo = { title: 'Test Todo', completed: false };
  const createdTodo = await result.current.fetch(TodoResource.getList.push, newTodo);

  expect(createdTodo.id).toBe(1);
});

Testing Error States

it('should handle fetch errors', async () => {
  const { result, waitFor } = renderDataHook(
    () => useSuspense(TodoResource.get, { id: 1 }),
    {
      initialFixtures: [
        {
          endpoint: TodoResource.get,
          args: [{ id: 1 }],
          response: null,
          error: new Error('Not found'),
        },
      ],
    },
  );

  await waitFor(() => {
    expect(result.current).toBeUndefined();
  });
});

Testing Components

Use MockResolver to provide fixture data when rendering components with DataProvider:

import { render } from '@testing-library/react';
import { DataProvider } from '@data-client/react';
import { MockResolver } from '@data-client/test';

it('should render todo list', async () => {
  const fixtures = [
    {
      endpoint: TodoResource.getList,
      args: [],
      response: [{ id: 1, title: 'Test Todo', completed: false }],
    },
  ];

  const { getByText } = render(
    <DataProvider>
      <MockResolver fixtures={fixtures}>
        <TodoList />
      </MockResolver>
    </DataProvider>,
  );

  expect(getByText('Test Todo')).toBeInTheDocument();
});

Testing with nock (HTTP Endpoint Testing)

import nock from 'nock';

it('should fetch data from API', async () => {
  const scope = nock('https://jsonplaceholder.typicode.com')
    .get('/todos/1')
    .reply(200, { id: 1, title: 'Test', completed: false });

  const result = await TodoResource.get({ id: 1 });

  expect(result.title).toBe('Test');
  scope.done();
});

Testing Managers

it('should handle manager middleware', async () => {
  const mockManager = {
    middleware: (controller) => (next) => async (action) => {
      if (action.type === 'FETCH') {
        console.log('Fetch action:', action);
      }
      return next(action);
    },
    cleanup: jest.fn(),
  };

  const { controller } = renderDataHook(
    () => useController(),
    { managers: [mockManager] },
  );

  await controller.fetch(TodoResource.get, { id: 1 });
  expect(mockManager.cleanup).not.toHaveBeenCalled();
});

Test File Organization

Keep tests under packages/*/src/**/__tests__:

packages/react/src/hooks/__tests__/useSuspense.test.ts
packages/react/src/components/__tests__/DataProvider.test.tsx

Test naming:

  • Node-only: *.node.test.ts[x]
  • React Native: *.native.test.ts[x]
  • Regular: *.test.ts[x]

Best Practices

  • Use renderDataHook() for testing hooks that use @data-client/react hooks
  • Use fixtures or interceptors when testing hooks or components
  • Use nock when testing networking definitions
  • Test both success and error scenarios
  • Test mutations and their side effects
  • Don't mock @data-client internals directly
  • Don't use raw fetch in tests when fixtures are available
  • Don't manually call renderDataHook.cleanup() in afterEach -- cleanup is automatic

References

For detailed API documentation, see the references directory:

Signals

GitHub stars
2k
Forks
99
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
data-client-react-testing
Source
github.com/reactive/data-client