Write Jest tests using TDD patterns and factory functions
A Jest testing-patterns skill covering TDD, factory functions, custom render wrappers, and GraphQL hook mocking.
Why it matters
Implement comprehensive unit tests following test-driven development methodology with reusable factory patterns, behavior-driven testing principles, and proper mocking strategies for React Native and GraphQL applications.
Outcomes
What it gets done
Create factory functions with sensible defaults and override capabilities for props and data
Mock GraphQL hooks and modules with proper jest patterns and assertions
Build custom render utilities that wrap components with required providers
Follow TDD red-green-refactor cycle with descriptive behavior-focused test names
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/ag-code-showcase-testing-patterns | bash Overview
Testing Patterns and Utilities
This skill covers Jest testing patterns: TDD red-green-refactor, factory functions for mock props and data, a custom render wrapper, and mocking modules and GraphQL hooks, avoiding mock-behavior and non-factory anti-patterns. Use it when writing unit tests, creating test factories, or following TDD for Jest-based React or React Native testing.
What it does
This skill covers Jest testing patterns for React and React Native: test-driven development, meaning writing a failing test first, implementing minimal code to pass, refactoring after green, and never writing production code without a failing test; behavior-driven testing, meaning testing behavior and public APIs rather than implementation details, with descriptive test names; and the factory pattern, meaning mock-data functions that provide sensible defaults with overridable properties to keep tests DRY. It provides a custom render utility wrapping components with required providers, such as a theme provider, for consistent rendering across tests. Factory examples cover component-props factories that spread sensible defaults plus overrides typed against the component's own props, and data factories such as a mock-user function with sensible id, name, email, and role defaults. Mocking patterns cover full module mocks, factory-function module mocks, accessing a mock through Jest's mock-requiring utility, and specifically mocking GraphQL query hooks by overriding their return value for data, loading, and error per test. Test structure uses nested describe blocks for rendering, user interactions, and edge cases, with mocks cleared before each test, and covers three query pattern categories: asserting an element exists, asserting it doesn't, and asserting async appearance through a wait-for helper. A full user-interaction example shows firing change and press events to drive a login form and waiting to confirm a submit callback fired.
Anti-patterns explicitly flagged include testing that a mock function was called instead of testing the actual resulting behavior - asserting on rendered output, not mock invocation - and not using factories, which leads to duplicated, inconsistent, and sometimes incomplete test data across tests, such as one test object missing a field another test includes. Best practices reinforce always using factory functions for props and data, testing behavior not implementation, descriptive test names, describe-block organization, clearing mocks between tests, and keeping each test focused on one behavior. Standard npm test commands cover running all tests, running with coverage, and running a specific file.
When to use - and when NOT to
Use it when writing unit tests, creating test factories, or following the TDD red-green-refactor cycle - for Jest-based React or React Native testing specifically.
Inputs and outputs
Given a component or data shape needing tests, it produces factory functions with sensible defaults, a custom render wrapper, structured describe and it test suites, and mocks for modules or GraphQL hooks configured per test case.
Integrations
Jest and React Native Testing Library utilities such as render, screen, fireEvent, and waitFor; GraphQL-generated hook mocking; and sibling skills covering testing all UI states and writing a reproducing test before fixing a bug.
Who it's for
Developers writing or maintaining a Jest test suite who want consistent, DRY test data via factories and behavior-focused assertions instead of duplicated fixtures and implementation-detail checks.
Source README
Testing Patterns and Utilities
When to Use
Use this skill when you need jest testing patterns, factory functions, mocking strategies, and TDD workflow. Use when writing unit tests, creating test factories, or following TDD red-green-refactor cycle.
Testing Philosophy
Test-Driven Development (TDD):
- Write failing test FIRST
- Implement minimal code to pass
- Refactor after green
- Never write production code without a failing test
Behavior-Driven Testing:
- Test behavior, not implementation
- Focus on public APIs and business requirements
- Avoid testing implementation details
- Use descriptive test names that describe behavior
Factory Pattern:
- Create
getMockX(overrides?: Partial<X>)functions - Provide sensible defaults
- Allow overriding specific properties
- Keep tests DRY and maintainable
Test Utilities
Custom Render Function
Create a custom render that wraps components with required providers:
// src/utils/testUtils.tsx
import { render } from '@testing-library/react-native';
import { ThemeProvider } from './theme';
export const renderWithTheme = (ui: React.ReactElement) => {
return render(
<ThemeProvider>{ui}</ThemeProvider>
);
};
Usage:
import { renderWithTheme } from 'utils/testUtils';
import { screen } from '@testing-library/react-native';
it('should render component', () => {
renderWithTheme(<MyComponent />);
expect(screen.getByText('Hello')).toBeTruthy();
});
Factory Pattern
Component Props Factory
import { ComponentProps } from 'react';
const getMockMyComponentProps = (
overrides?: Partial<ComponentProps<typeof MyComponent>>
) => {
return {
title: 'Default Title',
count: 0,
onPress: jest.fn(),
isLoading: false,
...overrides,
};
};
// Usage in tests
it('should render with custom title', () => {
const props = getMockMyComponentProps({ title: 'Custom Title' });
renderWithTheme(<MyComponent {...props} />);
expect(screen.getByText('Custom Title')).toBeTruthy();
});
Data Factory
interface User {
id: string;
name: string;
email: string;
role: 'admin' | 'user';
}
const getMockUser = (overrides?: Partial<User>): User => {
return {
id: '123',
name: 'John Doe',
email: 'john@example.com',
role: 'user',
...overrides,
};
};
// Usage
it('should display admin badge for admin users', () => {
const user = getMockUser({ role: 'admin' });
renderWithTheme(<UserCard user={user} />);
expect(screen.getByText('Admin')).toBeTruthy();
});
Mocking Patterns
Mocking Modules
// Mock entire module
jest.mock('utils/analytics');
// Mock with factory function
jest.mock('utils/analytics', () => ({
Analytics: {
logEvent: jest.fn(),
},
}));
// Access mock in test
const mockLogEvent = jest.requireMock('utils/analytics').Analytics.logEvent;
Mocking GraphQL Hooks
jest.mock('./GetItems.generated', () => ({
useGetItemsQuery: jest.fn(),
}));
const mockUseGetItemsQuery = jest.requireMock(
'./GetItems.generated'
).useGetItemsQuery as jest.Mock;
// In test
mockUseGetItemsQuery.mockReturnValue({
data: { items: [] },
loading: false,
error: undefined,
});
Test Structure
describe('ComponentName', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('Rendering', () => {
it('should render component with default props', () => {});
it('should render loading state when loading', () => {});
});
describe('User interactions', () => {
it('should call onPress when button is clicked', async () => {});
});
describe('Edge cases', () => {
it('should handle empty data gracefully', () => {});
});
});
Query Patterns
// Element must exist
expect(screen.getByText('Hello')).toBeTruthy();
// Element should not exist
expect(screen.queryByText('Goodbye')).toBeNull();
// Element appears asynchronously
await waitFor(() => {
expect(screen.findByText('Loaded')).toBeTruthy();
});
User Interaction Patterns
import { fireEvent, screen } from '@testing-library/react-native';
it('should submit form on button click', async () => {
const onSubmit = jest.fn();
renderWithTheme(<LoginForm onSubmit={onSubmit} />);
fireEvent.changeText(screen.getByLabelText('Email'), 'user@example.com');
fireEvent.changeText(screen.getByLabelText('Password'), 'password123');
fireEvent.press(screen.getByTestId('login-button'));
await waitFor(() => {
expect(onSubmit).toHaveBeenCalled();
});
});
Anti-Patterns to Avoid
Testing Mock Behavior Instead of Real Behavior
// Bad - testing the mock
expect(mockFetchData).toHaveBeenCalled();
// Good - testing actual behavior
expect(screen.getByText('John Doe')).toBeTruthy();
Not Using Factories
// Bad - duplicated, inconsistent test data
it('test 1', () => {
const user = { id: '1', name: 'John', email: 'john@test.com', role: 'user' };
});
it('test 2', () => {
const user = { id: '2', name: 'Jane', email: 'jane@test.com' }; // Missing role!
});
// Good - reusable factory
const user = getMockUser({ name: 'Custom Name' });
Best Practices
- Always use factory functions for props and data
- Test behavior, not implementation
- Use descriptive test names
- Organize with describe blocks
- Clear mocks between tests
- Keep tests focused - one behavior per test
Running Tests
### Run all tests
npm test
### Run with coverage
npm run test:coverage
### Run specific file
npm test ComponentName.test.tsx
Integration with Other Skills
- react-ui-patterns: Test all UI states (loading, error, empty, success)
- systematic-debugging: Write test that reproduces bug before fixing
Limitations
- Use this skill only when the task clearly matches its upstream source and local project context.
- Verify commands, generated code, dependencies, credentials, and external service behavior before applying changes.
- Do not treat examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.
FAQ
Common questions
Discussion
Questions & comments · 0
Sign In Sign in to leave a comment.