Add 260+ Claude Code skills from skills.sh
Complete collection of AI agent skills including: - Frontend Development (Vue, React, Next.js, Three.js) - Backend Development (NestJS, FastAPI, Node.js) - Mobile Development (React Native, Expo) - Testing (E2E, frontend, webapp) - DevOps (GitHub Actions, CI/CD) - Marketing (SEO, copywriting, analytics) - Security (binary analysis, vulnerability scanning) - And many more... Synchronized from: https://skills.sh/ Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
259
testing-patterns/skill.md
Normal file
259
testing-patterns/skill.md
Normal file
@@ -0,0 +1,259 @@
|
||||
---
|
||||
name: testing-patterns
|
||||
description: 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 Patterns and Utilities
|
||||
|
||||
## 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:
|
||||
|
||||
```typescript
|
||||
// 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:**
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
// Bad - testing the mock
|
||||
expect(mockFetchData).toHaveBeenCalled();
|
||||
|
||||
// Good - testing actual behavior
|
||||
expect(screen.getByText('John Doe')).toBeTruthy();
|
||||
```
|
||||
|
||||
### Not Using Factories
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
Reference in New Issue
Block a user