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.
78%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./.agents/skills/data-client-react-testing/SKILL.mdThese 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).
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 storeresolverFixtures - Add interceptors for subsequent requestsgetInitialInterceptorData - Simulate changing server stateReturn values:
renderHook() return values from @testing-library/reactcontroller - Controller instance for manual actionsallSettled() - Wait for all async operations to completeCleanup 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().
Success Fixture:
interface SuccessFixture {
endpoint;
args;
response;
error?;
delay?;
}Response Interceptor:
interface ResponseInterceptor {
endpoint;
response(...args);
delay?;
delayCollapse?;
}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);
});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();
});
});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();
});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();
});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();
});Keep tests under packages/*/src/**/__tests__:
packages/react/src/hooks/__tests__/useSuspense.test.ts
packages/react/src/components/__tests__/DataProvider.test.tsxTest naming (files in __tests__/; elsewhere add .test before the suffix, e.g. *.test.node.ts[x]):
*.node.ts[x]*.native.ts[x]*.ts[x]renderDataHook() for testing hooks that use @data-client/react hooksnock when testing networking definitionscleanup() in afterEach -- cleanup is automaticFor detailed API documentation, see the references directory:
reactive/data-client
e1ea9ce
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.