Claude
Skills
Sign in
Back

jest-testing

Included with Lifetime
$97 forever

Automatically activated when user works with Jest tests, mentions Jest configuration, asks about Jest matchers/mocks, or has files matching *.test.js, *.test.ts, jest.config.*. Provides Jest-specific expertise for testing React, Node.js, and JavaScript applications. Also applies to Vitest due to API compatibility. Does NOT handle general quality analysis - use analyzing-test-quality for that.

Web Devscriptsassets

What this skill does


# Jest Testing Expertise

You are an expert in Jest testing framework with deep knowledge of its configuration, matchers, mocks, and best practices for testing JavaScript and TypeScript applications.

## Your Capabilities

1. **Jest Configuration**: Setup, configuration files, environments, and presets
2. **Matchers & Assertions**: Built-in and custom matchers, asymmetric matchers
3. **Mocking**: Mock functions, modules, timers, and external dependencies
4. **Snapshot Testing**: Inline and external snapshots, snapshot updates
5. **Code Coverage**: Coverage configuration, thresholds, and reports
6. **Test Organization**: Describe blocks, hooks, test filtering
7. **React Testing**: Testing React components with Jest DOM and RTL

## When to Use This Skill

Claude should automatically invoke this skill when:
- The user mentions Jest, jest.config, or Jest-specific features
- Files matching `*.test.js`, `*.test.ts`, `*.test.jsx`, `*.test.tsx` are encountered
- The user asks about mocking, snapshots, or Jest matchers
- The conversation involves testing React, Node.js, or JavaScript apps
- Jest configuration or setup is discussed

## How to Use This Skill

### Accessing Resources

Use `{baseDir}` to reference files in this skill directory:
- Scripts: `{baseDir}/scripts/`
- Documentation: `{baseDir}/references/`
- Templates: `{baseDir}/assets/`

### Progressive Discovery

1. Start with core Jest expertise
2. Reference specific documentation as needed
3. Provide code examples from templates

## Available Resources

This skill includes ready-to-use resources in `{baseDir}`:

- **references/jest-cheatsheet.md** - Quick reference for matchers, mocks, async patterns, and CLI commands
- **assets/test-file.template.ts** - Complete test templates for unit tests, async tests, class tests, mock tests, React components, and hooks
- **scripts/check-jest-setup.sh** - Validates Jest configuration and dependencies

## Jest Best Practices

### Test Structure
```javascript
describe('ComponentName', () => {
  beforeEach(() => {
    // Setup
  });

  afterEach(() => {
    // Cleanup
  });

  describe('method or behavior', () => {
    it('should do expected thing when condition', () => {
      // Arrange
      // Act
      // Assert
    });
  });
});
```

### Mocking Patterns

#### Mock Functions
```javascript
const mockFn = jest.fn();
mockFn.mockReturnValue('value');
mockFn.mockResolvedValue('async value');
mockFn.mockImplementation((arg) => arg * 2);
```

#### Mock Modules
```javascript
jest.mock('./module', () => ({
  func: jest.fn().mockReturnValue('mocked'),
}));
```

#### Mock Timers
```javascript
jest.useFakeTimers();
jest.advanceTimersByTime(1000);
jest.runAllTimers();
```

### Common Matchers
```javascript
expect(value).toBe(expected);          // Strict equality
expect(value).toEqual(expected);       // Deep equality
expect(value).toBeTruthy();            // Truthy
expect(value).toContain(item);         // Array/string contains
expect(fn).toHaveBeenCalledWith(args); // Function called with
expect(value).toMatchSnapshot();       // Snapshot
expect(fn).toThrow(error);             // Throws
```

### Async Testing
```javascript
// Promises
it('async test', async () => {
  await expect(asyncFn()).resolves.toBe('value');
});

// Callbacks
it('callback test', (done) => {
  callbackFn((result) => {
    expect(result).toBe('value');
    done();
  });
});
```

## Jest Configuration

### Basic Configuration
```javascript
// jest.config.js
module.exports = {
  testEnvironment: 'node', // or 'jsdom'
  roots: ['<rootDir>/src'],
  testMatch: ['**/__tests__/**/*.ts', '**/*.test.ts'],
  transform: {
    '^.+\\.tsx?$': 'ts-jest',
  },
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1',
  },
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80,
    },
  },
};
```

## React Testing Library

### Setup with Custom Render
```typescript
// test-utils.tsx
import { render, RenderOptions } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { BrowserRouter } from 'react-router-dom';

const AllProviders = ({ children }: { children: React.ReactNode }) => {
  const queryClient = new QueryClient({
    defaultOptions: { queries: { retry: false } },
  });

  return (
    <QueryClientProvider client={queryClient}>
      <BrowserRouter>
        {children}
      </BrowserRouter>
    </QueryClientProvider>
  );
};

export const renderWithProviders = (
  ui: React.ReactElement,
  options?: RenderOptions
) => render(ui, { wrapper: AllProviders, ...options });

export * from '@testing-library/react';
```

### Query Priority (Best to Worst)
```typescript
// 1. Accessible queries (best)
screen.getByRole('button', { name: 'Submit' });
screen.getByLabelText('Email');
screen.getByPlaceholderText('Enter email');
screen.getByText('Welcome');

// 2. Semantic queries
screen.getByAltText('Profile picture');
screen.getByTitle('Close');

// 3. Test IDs (last resort)
screen.getByTestId('submit-button');
```

### User Interactions
```typescript
import userEvent from '@testing-library/user-event';

test('form submission', async () => {
  const user = userEvent.setup();
  render(<LoginForm />);

  // Type in inputs
  await user.type(screen.getByLabelText('Email'), '[email protected]');
  await user.type(screen.getByLabelText('Password'), 'password123');

  // Click button
  await user.click(screen.getByRole('button', { name: 'Sign in' }));

  // Check result
  await waitFor(() => {
    expect(screen.getByText('Welcome!')).toBeInTheDocument();
  });
});

test('keyboard navigation', async () => {
  const user = userEvent.setup();
  render(<Form />);

  await user.tab(); // Focus first element
  await user.keyboard('{Enter}'); // Press enter
  await user.keyboard('[ShiftLeft>][Tab][/ShiftLeft]'); // Shift+Tab
});
```

### Testing Hooks
```typescript
import { renderHook, act } from '@testing-library/react';
import { useCounter } from './useCounter';

test('useCounter increments', () => {
  const { result } = renderHook(() => useCounter());

  expect(result.current.count).toBe(0);

  act(() => {
    result.current.increment();
  });

  expect(result.current.count).toBe(1);
});

// With wrapper for context
test('hook with context', () => {
  const wrapper = ({ children }) => (
    <ThemeProvider theme="dark">{children}</ThemeProvider>
  );

  const { result } = renderHook(() => useTheme(), { wrapper });
  expect(result.current.theme).toBe('dark');
});
```

### Async Assertions
```typescript
import { waitFor, waitForElementToBeRemoved } from '@testing-library/react';

test('async loading', async () => {
  render(<DataFetcher />);

  // Wait for loading to disappear
  await waitForElementToBeRemoved(() => screen.queryByText('Loading...'));

  // Wait for content
  await waitFor(() => {
    expect(screen.getByText('Data loaded')).toBeInTheDocument();
  });

  // With timeout
  await waitFor(
    () => expect(screen.getByText('Slow content')).toBeInTheDocument(),
    { timeout: 5000 }
  );
});
```

## Network Mocking with MSW

### Setup
```typescript
// src/mocks/handlers.ts
import { http, HttpResponse } from 'msw';

export const handlers = [
  http.get('/api/users', () => {
    return HttpResponse.json([
      { id: 1, name: 'John' },
      { id: 2, name: 'Jane' },
    ]);
  }),

  http.post('/api/users', async ({ request }) => {
    const body = await request.json();
    return HttpResponse.json({ id: 3, ...body }, { status: 201 });
  }),

  http.delete('/api/users/:id', ({ params }) => {
    return HttpResponse.json({ deleted: params.id });
  }),
];

// src/mocks/server.ts
import { setupServer } from 'msw/node';
import { handlers } from './handlers';

export const server = setupServer(...handlers);
```

### Jest Setup
```typescript
// jest.setup.ts
import { server } from './src/mocks/server';

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afte

Related in Web Dev