Claude
Skills
Sign in
Back

jest-advanced

Included with Lifetime
$97 forever

Use when advanced Jest features including custom matchers, parameterized tests with test.each, coverage configuration, and performance optimization.

Code Review

What this skill does


# Jest Advanced

Master advanced Jest features including custom matchers, parameterized tests with test.each, coverage configuration, and performance optimization. This skill covers sophisticated testing techniques for complex scenarios and large test suites.

## Custom Matchers

### Creating Custom Matchers

```javascript
// matchers/toBeWithinRange.js
export function toBeWithinRange(received, floor, ceiling) {
  const pass = received >= floor && received <= ceiling;
  if (pass) {
    return {
      message: () =>
        `expected ${received} not to be within range ${floor} - ${ceiling}`,
      pass: true
    };
  } else {
    return {
      message: () =>
        `expected ${received} to be within range ${floor} - ${ceiling}`,
      pass: false
    };
  }
}

// jest.setup.js
import { toBeWithinRange } from './matchers/toBeWithinRange';

expect.extend({
  toBeWithinRange
});

// test file
describe('Custom matcher', () => {
  it('should check if number is within range', () => {
    expect(5).toBeWithinRange(1, 10);
    expect(15).not.toBeWithinRange(1, 10);
  });
});
```

### Async Custom Matcher

```javascript
// matchers/toResolveWithin.js
export async function toResolveWithin(received, timeout) {
  const startTime = Date.now();
  try {
    await received;
    const duration = Date.now() - startTime;
    const pass = duration <= timeout;

    return {
      message: () =>
        pass
          ? `expected promise not to resolve within ${timeout}ms (resolved in ${duration}ms)`
          : `expected promise to resolve within ${timeout}ms (took ${duration}ms)`,
      pass
    };
  } catch (error) {
    return {
      message: () => `expected promise to resolve but it rejected with ${error}`,
      pass: false
    };
  }
}

// Usage
expect.extend({ toResolveWithin });

it('should resolve quickly', async () => {
  await expect(fetchData()).toResolveWithin(1000);
});
```

### Type-Safe Custom Matchers (TypeScript)

```typescript
// matchers/index.ts
interface CustomMatchers<R = unknown> {
  toBeWithinRange(floor: number, ceiling: number): R;
  toHaveValidEmail(): R;
}

declare global {
  namespace jest {
    interface Expect extends CustomMatchers {}
    interface Matchers<R> extends CustomMatchers<R> {}
    interface InverseAsymmetricMatchers extends CustomMatchers {}
  }
}

export function toBeWithinRange(
  this: jest.MatcherContext,
  received: number,
  floor: number,
  ceiling: number
): jest.CustomMatcherResult {
  const pass = received >= floor && received <= ceiling;
  return {
    message: () =>
      pass
        ? `expected ${received} not to be within range ${floor} - ${ceiling}`
        : `expected ${received} to be within range ${floor} - ${ceiling}`,
    pass
  };
}

export function toHaveValidEmail(
  this: jest.MatcherContext,
  received: string
): jest.CustomMatcherResult {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  const pass = emailRegex.test(received);
  return {
    message: () =>
      pass
        ? `expected ${received} not to be a valid email`
        : `expected ${received} to be a valid email`,
    pass
  };
}

// jest.setup.ts
import * as matchers from './matchers';
expect.extend(matchers);
```

## Parameterized Tests

### test.each with Arrays

```javascript
describe('Addition', () => {
  test.each([
    [1, 1, 2],
    [1, 2, 3],
    [2, 1, 3],
  ])('add(%i, %i) should equal %i', (a, b, expected) => {
    expect(add(a, b)).toBe(expected);
  });
});
```

### test.each with Objects

```javascript
describe('User validation', () => {
  test.each([
    { email: '[email protected]', valid: true },
    { email: 'invalid', valid: false },
    { email: 'test@', valid: false },
    { email: '@example.com', valid: false },
  ])('validateEmail($email) should return $valid', ({ email, valid }) => {
    expect(validateEmail(email)).toBe(valid);
  });
});
```

### test.each with Template Literals

```javascript
describe('String operations', () => {
  test.each`
    input        | method      | expected
    ${'hello'}   | ${'upper'}  | ${'HELLO'}
    ${'WORLD'}   | ${'lower'}  | ${'world'}
    ${'HeLLo'}   | ${'title'}  | ${'Hello'}
  `('transform $input using $method should return $expected',
    ({ input, method, expected }) => {
      expect(transform(input, method)).toBe(expected);
    }
  );
});
```

### describe.each for Multiple Test Suites

```javascript
describe.each([
  { browser: 'Chrome', version: 90 },
  { browser: 'Firefox', version: 88 },
  { browser: 'Safari', version: 14 },
])('Browser compatibility - $browser', ({ browser, version }) => {
  it(`should support ${browser} version ${version}`, () => {
    expect(isSupported(browser, version)).toBe(true);
  });

  it(`should handle ${browser} specific features`, () => {
    expect(getFeatures(browser)).toBeDefined();
  });
});
```

## Coverage Configuration

### Advanced Coverage Settings

```javascript
// jest.config.js
module.exports = {
  collectCoverage: true,
  coverageDirectory: 'coverage',
  coverageReporters: ['text', 'lcov', 'html', 'json-summary'],

  collectCoverageFrom: [
    'src/**/*.{js,jsx,ts,tsx}',
    '!src/**/*.d.ts',
    '!src/**/*.stories.{js,jsx,ts,tsx}',
    '!src/**/__tests__/**',
    '!src/**/types/**',
    '!src/index.{js,ts}',
  ],

  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80
    },
    './src/core/': {
      branches: 90,
      functions: 95,
      lines: 95,
      statements: 95
    },
    './src/utils/': {
      branches: 85,
      functions: 90,
      lines: 90,
      statements: 90
    }
  },

  coveragePathIgnorePatterns: [
    '/node_modules/',
    '/dist/',
    '/build/',
    '/__mocks__/',
    '/coverage/'
  ]
};
```

### Custom Coverage Reporter

```javascript
// custom-reporter.js
class CustomCoverageReporter {
  constructor(globalConfig, options) {
    this._globalConfig = globalConfig;
    this._options = options;
  }

  onRunComplete(contexts, results) {
    const { coverageMap } = results;

    if (!coverageMap) {
      return;
    }

    const summary = coverageMap.getCoverageSummary();
    const metrics = {
      lines: summary.lines.pct,
      statements: summary.statements.pct,
      functions: summary.functions.pct,
      branches: summary.branches.pct
    };

    console.log('\nCoverage Summary:');
    console.log(`Lines:      ${metrics.lines}%`);
    console.log(`Statements: ${metrics.statements}%`);
    console.log(`Functions:  ${metrics.functions}%`);
    console.log(`Branches:   ${metrics.branches}%`);

    // Send to external service
    if (this._options.webhook) {
      this.sendToWebhook(metrics, this._options.webhook);
    }
  }

  async sendToWebhook(metrics, url) {
    // Implementation
  }
}

module.exports = CustomCoverageReporter;

// jest.config.js
module.exports = {
  coverageReporters: [
    'text',
    ['./custom-reporter.js', { webhook: 'https://example.com/coverage' }]
  ]
};
```

## Performance Optimization

### Running Tests in Parallel

```javascript
// jest.config.js
module.exports = {
  maxWorkers: '50%', // Use 50% of available CPU cores
  // or specify a number
  // maxWorkers: 4,

  // Parallelize tests within a file
  maxConcurrency: 5,

  // Cache directory
  cacheDirectory: '.jest-cache',

  // Run tests in band for debugging
  // runInBand: false
};
```

### Selective Test Execution

```javascript
// Run only tests that changed
// package.json
{
  "scripts": {
    "test:changed": "jest --onlyChanged",
    "test:related": "jest --findRelatedTests src/modified-file.js"
  }
}
```

### Test Sharding for CI

```bash
# Split tests across multiple CI machines
jest --shard=1/3  # Run first third
jest --shard=2/3  # Run second third
jest --shard=3/3  # Run last third
```

### Bail on First Failure

```javascript
// jest.config.js
module.exports = {
  bail: 1, // Stop after first failure
  // bail: true, // Stop after any failure
};
```

## Advanced Mocking

### Mock Implementations with Different Return Values

```javascr

Related in Code Review