jest-testing

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.

25 stars

Best use case

jest-testing is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

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.

Teams using jest-testing should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/jest-testing/SKILL.md --create-dirs "https://raw.githubusercontent.com/ComeOnOliver/skillshub/main/skills/aiskillstore/marketplace/c0ntr0lledcha0s/jest-testing/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/jest-testing/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How jest-testing Compares

Feature / Agentjest-testingStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

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.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

Related Guides

SKILL.md Source

# 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'), 'test@example.com');
  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());
afterAll(() => server.close());
```

### Test-Specific Handlers
```typescript
import { server } from '../mocks/server';
import { http, HttpResponse } from 'msw';

test('handles error response', async () => {
  // Override for this test only
  server.use(
    http.get('/api/users', () => {
      return HttpResponse.json(
        { error: 'Server error' },
        { status: 500 }
      );
    })
  );

  render(<UserList />);

  await waitFor(() => {
    expect(screen.getByText('Failed to load users')).toBeInTheDocument();
  });
});

test('handles network error', async () => {
  server.use(
    http.get('/api/users', () => {
      return HttpResponse.error();
    })
  );

  render(<UserList />);

  await waitFor(() => {
    expect(screen.getByText('Network error')).toBeInTheDocument();
  });
});
```

### Request Assertions
```typescript
test('sends correct request', async () => {
  let capturedRequest: Request | null = null;

  server.use(
    http.post('/api/users', async ({ request }) => {
      capturedRequest = request.clone();
      return HttpResponse.json({ id: 1 });
    })
  );

  render(<CreateUserForm />);

  await userEvent.type(screen.getByLabelText('Name'), 'John');
  await userEvent.click(screen.getByRole('button', { name: 'Create' }));

  await waitFor(() => {
    expect(capturedRequest).not.toBeNull();
  });

  const body = await capturedRequest!.json();
  expect(body).toEqual({ name: 'John' });
});
```

## Custom Matchers

### Creating Custom Matchers
```typescript
// jest.setup.ts
expect.extend({
  toBeWithinRange(received: number, floor: number, ceiling: number) {
    const pass = received >= floor && received <= ceiling;
    return {
      pass,
      message: () =>
        pass
          ? `expected ${received} not to be within range ${floor} - ${ceiling}`
          : `expected ${received} to be within range ${floor} - ${ceiling}`,
    };
  },

  toHaveBeenCalledOnceWith(received: jest.Mock, ...args: unknown[]) {
    const pass =
      received.mock.calls.length === 1 &&
      JSON.stringify(received.mock.calls[0]) === JSON.stringify(args);
    return {
      pass,
      message: () =>
        pass
          ? `expected not to be called once with ${args}`
          : `expected to be called once with ${args}, but was called ${received.mock.calls.length} times`,
    };
  },
});

// Type declarations
declare global {
  namespace jest {
    interface Matchers<R> {
      toBeWithinRange(floor: number, ceiling: number): R;
      toHaveBeenCalledOnceWith(...args: unknown[]): R;
    }
  }
}
```

### Asymmetric Matchers
```typescript
test('asymmetric matchers', () => {
  const data = {
    id: 123,
    name: 'Test',
    createdAt: new Date().toISOString(),
  };

  expect(data).toEqual({
    id: expect.any(Number),
    name: expect.stringContaining('Test'),
    createdAt: expect.stringMatching(/^\d{4}-\d{2}-\d{2}/),
  });

  expect(['a', 'b', 'c']).toEqual(
    expect.arrayContaining(['a', 'c'])
  );

  expect({ a: 1, b: 2, c: 3 }).toEqual(
    expect.objectContaining({ a: 1, b: 2 })
  );
});
```

## Debugging Jest Tests

### Debug Output
```typescript
import { screen } from '@testing-library/react';

test('debugging', () => {
  render(<MyComponent />);

  // Print DOM
  screen.debug();

  // Print specific element
  screen.debug(screen.getByRole('button'));

  // Get readable DOM
  console.log(prettyDOM(container));
});
```

### Finding Slow Tests
```bash
# Run with verbose timing
jest --verbose

# Detect open handles
jest --detectOpenHandles

# Run tests serially to find interactions
jest --runInBand
```

### Common Debug Patterns
```typescript
// Check what's in the DOM
test('debug queries', () => {
  render(<MyComponent />);

  // Log all available roles
  screen.getByRole(''); // Will error with available roles

  // Check accessible name
  screen.logTestingPlaygroundURL(); // Opens playground
});

// Debug async issues
test('async debug', async () => {
  render(<AsyncComponent />);

  // Use findBy for async elements
  const element = await screen.findByText('Loaded');

  // Log state at each step
  screen.debug();
});
```

## CI/CD Integration

### GitHub Actions Workflow
```yaml
# .github/workflows/test.yml
name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test -- --coverage --ci

      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage/lcov.info
```

### Jest CI Configuration
```javascript
// jest.config.js
module.exports = {
  // ... other config

  // CI-specific settings
  ...(process.env.CI && {
    maxWorkers: 2,
    ci: true,
    coverageReporters: ['lcov', 'text-summary'],
  }),

  // Coverage thresholds
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80,
    },
  },
};
```

### Caching Dependencies
```yaml
# In GitHub Actions
- name: Cache Jest
  uses: actions/cache@v3
  with:
    path: |
      node_modules/.cache/jest
    key: jest-${{ runner.os }}-${{ hashFiles('**/jest.config.js') }}
```

## Common Issues & Solutions

### Issue: Tests are slow
- Use `jest.mock()` for expensive modules
- Run tests in parallel with `--maxWorkers`
- Use `beforeAll` for expensive setup
- Mock network requests with MSW

### Issue: Flaky tests
- Mock timers for timing-dependent code
- Use `waitFor` for async state changes
- Avoid shared mutable state
- Use `findBy` queries for async elements

### Issue: Mock not working
- Ensure mock is before import
- Use `jest.resetModules()` between tests
- Check module path matches exactly
- Use `jest.doMock()` for dynamic mocks

### Issue: Memory leaks
- Clean up in `afterEach`
- Mock timers with `jest.useFakeTimers()`
- Use `--detectLeaks` flag
- Check for unresolved promises

## Examples

### Example 1: Testing a React Component
When testing React components:
1. Check for React Testing Library usage
2. Verify proper queries (getByRole, getByLabelText)
3. Test user interactions with userEvent
4. Assert on accessible elements

### Example 2: Testing API Calls
When testing code that makes API calls:
1. Mock fetch or axios at module level
2. Test success and error scenarios
3. Verify request parameters
4. Test loading states

## Version Compatibility

The patterns in this skill require the following minimum versions:

| Package | Minimum Version | Features Used |
|---------|----------------|---------------|
| Jest | 29.0+ | Modern mock APIs, ESM support |
| @testing-library/react | 14.0+ | renderHook in main package |
| @testing-library/user-event | 14.0+ | userEvent.setup() API |
| msw | 2.0+ | http, HttpResponse (v1 used rest, ctx) |
| @testing-library/jest-dom | 6.0+ | Modern matchers |

### Migration Notes

**MSW v1 → v2**:
```typescript
// v1 (deprecated)
import { rest } from 'msw';
rest.get('/api', (req, res, ctx) => res(ctx.json(data)));

// v2 (current)
import { http, HttpResponse } from 'msw';
http.get('/api', () => HttpResponse.json(data));
```

**user-event v13 → v14**:
```typescript
// v13 (deprecated)
userEvent.click(button);

// v14 (current)
const user = userEvent.setup();
await user.click(button);
```

## Important Notes

- Jest is automatically invoked by Claude when relevant
- Always check for jest.config.js/ts for project-specific settings
- Use `{baseDir}` variable to reference skill resources
- Prefer Testing Library queries over direct DOM access for React

Related Skills

Testing Handbook Skills

25
from ComeOnOliver/skillshub

Comprehensive security testing toolkit generated from the [Trail of Bits Application Security Testing Handbook](https://appsec.guide/).

Constant-Time Testing

25
from ComeOnOliver/skillshub

Timing attacks exploit variations in execution time to extract secret information from cryptographic implementations. Unlike cryptanalysis that targets theoretical weaknesses, timing attacks leverage implementation flaws - and they can affect any cryptographic code.

go-testing

25
from ComeOnOliver/skillshub

Use when writing, reviewing, or improving Go test code — including table-driven tests, subtests, parallel tests, test helpers, test doubles, and assertions with cmp.Diff. Also use when a user asks to write a test for a Go function, even if they don't mention specific patterns like table-driven tests or subtests. Does not cover benchmark performance testing (see go-performance).

robotics-testing

25
from ComeOnOliver/skillshub

Testing strategies, patterns, and tools for robotics software. Use this skill when writing unit tests, integration tests, simulation tests, or hardware-in-the-loop tests for robot systems. Trigger whenever the user mentions testing ROS nodes, pytest with ROS, launch_testing, simulation testing, CI/CD for robotics, test fixtures for sensors, mock hardware, deterministic replay, regression testing for robot behaviors, or validating perception/planning/control pipelines. Also covers property-based testing for kinematics, fuzz testing for message handlers, and golden-file testing for trajectories.

testing-strategies

25
from ComeOnOliver/skillshub

Design comprehensive testing strategies for software quality assurance. Use when planning test coverage, implementing test pyramids, or setting up testing infrastructure. Handles unit testing, integration testing, E2E testing, TDD, and testing best practices.

backend-testing

25
from ComeOnOliver/skillshub

Write comprehensive backend tests including unit tests, integration tests, and API tests. Use when testing REST APIs, database operations, authentication flows, or business logic. Handles Jest, Pytest, Mocha, testing strategies, mocking, and test coverage.

wordpress-penetration-testing

25
from ComeOnOliver/skillshub

This skill should be used when the user asks to "pentest WordPress sites", "scan WordPress for vulnerabilities", "enumerate WordPress users, themes, or plugins", "exploit WordPress vulnerabilities", or "use WPScan". It provides comprehensive WordPress security assessment methodologies.

web3-testing

25
from ComeOnOliver/skillshub

Test smart contracts comprehensively using Hardhat and Foundry with unit tests, integration tests, and mainnet forking. Use when testing Solidity contracts, setting up blockchain test suites, or validating DeFi protocols.

web-security-testing

25
from ComeOnOliver/skillshub

Web application security testing workflow for OWASP Top 10 vulnerabilities including injection, XSS, authentication flaws, and access control issues.

unit-testing-test-generate

25
from ComeOnOliver/skillshub

Generate comprehensive, maintainable unit tests across languages with strong coverage and edge case focus.

testing-qa

25
from ComeOnOliver/skillshub

Comprehensive testing and QA workflow covering unit testing, integration testing, E2E testing, browser automation, and quality assurance.

temporal-python-testing

25
from ComeOnOliver/skillshub

Test Temporal workflows with pytest, time-skipping, and mocking strategies. Covers unit testing, integration testing, replay testing, and local development setup. Use when implementing Temporal workflow tests or debugging test failures.