Skill

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.

Works with jestreactgraphql

76
Spark score
out of 100
Updated 6 months ago
Version 1.0.0

Add to Favorites

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

01

Create factory functions with sensible defaults and override capabilities for props and data

02

Mock GraphQL hooks and modules with proper jest patterns and assertions

03

Build custom render utilities that wrap components with required providers

04

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

  1. Always use factory functions for props and data
  2. Test behavior, not implementation
  3. Use descriptive test names
  4. Organize with describe blocks
  5. Clear mocks between tests
  6. 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.