CtrlK
BlogDocsLog inGet started
Tessl Logo
SkillPublic

data-client-react-testing

Test React code that uses @data-client/react: components and hooks that fetch or change data, with mocked responses for loading, error, mutation and polling cases. Covers renderDataHook, MockResolver and fixtures.

Quality

78%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide
SecuritybySnyk

Passed

No findings from the security scan

Fix and improve this skill with Tessl

tessl review fix ./.agents/skills/data-client-react-testing/SKILL.md
SKILL.md
Quality
Evals
Security

Testing Patterns (@data-client/test)

These helpers come from @data-client/test. If package.json doesn't list it, add it as a dev dependency with the project's package manager (npm install -D @data-client/test).

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. Call the cleanup() a renderDataHook() call returns only to control cleanup order, such as before jest.useRealTimers().

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

Pass managers a function that returns new Managers; it runs once per renderDataHook() call. They replace the defaults and must include a NetworkManager. Put your Manager before NetworkManager to see fetch actions: NetworkManager handles them without passing them on.

import { NetworkManager, useController } from '@data-client/react';
import type { Manager } from '@data-client/react';

it('should handle manager middleware', async () => {
  const types: string[] = [];
  const mockManager: Manager = {
    middleware: () => (next) => (action) => {
      types.push(action.type);
      return next(action);
    },
    cleanup: jest.fn(),
  };

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

  await controller.setResponse(
    TodoResource.get,
    { id: 1 },
    { id: 1, title: 'Test', completed: false },
  );
  expect(types).toEqual(['rdc/setresponse']);

  cleanup();
  expect(mockManager.cleanup).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 (files in __tests__/; elsewhere add .test before the suffix, e.g. *.test.node.ts[x]):

  • Node-only: *.node.ts[x]
  • React Native: *.native.ts[x]
  • Regular: *.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 call cleanup() in afterEach -- cleanup is automatic

References

For detailed API documentation, see the references directory:

Repository

reactive/data-client

Path

.agents/skills/data-client-react-testing/SKILL.md

Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.