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.
Test-Driven Development (TDD):
Behavior-Driven Testing:
Factory Pattern:
getMockX(overrides?: Partial<X>) functionsCreate 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();
});
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();
});
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();
});
// 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;
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,
});
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', () => {});
});
});
// 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();
});
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();
});
});
// Bad - testing the mock
expect(mockFetchData).toHaveBeenCalled();
// Good - testing actual behavior
expect(screen.getByText('John Doe')).toBeTruthy();
// 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' });
# Run all tests
npm test
# Run with coverage
npm run test:coverage
# Run specific file
npm test ComponentName.test.tsx
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer