Claude-skill-registry jest
Write JavaScript/TypeScript tests with Jest including unit tests, mocking, snapshots, and coverage. Test React components, async code, and Node.js. Use for JavaScript testing, React testing, or test automation.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/jest-fgarofalo56-suppercharge-microso" ~/.claude/skills/majiayu000-claude-skill-registry-jest && rm -rf "$T"
manifest:
skills/data/jest-fgarofalo56-suppercharge-microso/SKILL.mdsource content
Jest Testing Framework
Quick Reference
| Command | Purpose |
|---|---|
| Run all tests |
| Watch mode |
| Generate coverage |
| Run specific test |
| Run matching tests |
1. Setup
Installation
# JavaScript npm install --save-dev jest # TypeScript npm install --save-dev jest ts-jest @types/jest npx ts-jest config:init # React npm install --save-dev @testing-library/react @testing-library/jest-dom
Configuration (jest.config.js)
module.exports = { // Test environment testEnvironment: 'node', // or 'jsdom' for browser // TypeScript preset: 'ts-jest', // Test file patterns testMatch: ['**/__tests__/**/*.test.[jt]s?(x)'], // Coverage collectCoverageFrom: ['src/**/*.{js,ts,tsx}', '!src/**/*.d.ts'], coverageThreshold: { global: { branches: 80, functions: 80, lines: 80, statements: 80 } }, // Module resolution moduleNameMapper: { '^@/(.*)$': '<rootDir>/src/$1', '\\.(css|less|scss)$': 'identity-obj-proxy' }, // Setup files setupFilesAfterEnv: ['<rootDir>/jest.setup.js'], // Transform transform: { '^.+\\.(ts|tsx)$': 'ts-jest' } };
Setup File (jest.setup.js)
import '@testing-library/jest-dom'; // Global mocks global.fetch = jest.fn(); // Cleanup after each test afterEach(() => { jest.clearAllMocks(); });
2. Basic Tests
Test Structure
describe('Calculator', () => { // Setup before all tests beforeAll(() => { console.log('Starting Calculator tests'); }); // Setup before each test beforeEach(() => { // Reset state }); // Cleanup after each test afterEach(() => { jest.clearAllMocks(); }); // Cleanup after all tests afterAll(() => { console.log('Finished Calculator tests'); }); describe('add', () => { it('adds two positive numbers', () => { expect(add(2, 3)).toBe(5); }); it('adds negative numbers', () => { expect(add(-1, -2)).toBe(-3); }); it.each([ [1, 2, 3], [0, 0, 0], [-1, 1, 0] ])('adds %i + %i to equal %i', (a, b, expected) => { expect(add(a, b)).toBe(expected); }); }); // Skip test it.skip('skipped test', () => { // This test is skipped }); // Only run this test it.only('only this test runs', () => { // Only this test runs in this file }); });
Matchers
// Equality expect(value).toBe(expected); // Strict equality expect(value).toEqual(expected); // Deep equality expect(value).toStrictEqual(expected); // Strict deep equality // Truthiness expect(value).toBeTruthy(); expect(value).toBeFalsy(); expect(value).toBeNull(); expect(value).toBeUndefined(); expect(value).toBeDefined(); // Numbers expect(value).toBeGreaterThan(3); expect(value).toBeGreaterThanOrEqual(3); expect(value).toBeLessThan(5); expect(value).toBeCloseTo(0.3, 5); // Floating point // Strings expect(string).toMatch(/regex/); expect(string).toContain('substring'); // Arrays expect(array).toContain(item); expect(array).toHaveLength(3); expect(array).toContainEqual({ id: 1 }); // Deep equality // Objects expect(obj).toHaveProperty('key'); expect(obj).toHaveProperty('key', 'value'); expect(obj).toMatchObject({ subset: true }); // Exceptions expect(() => throwError()).toThrow(); expect(() => throwError()).toThrow('error message'); expect(() => throwError()).toThrow(ErrorClass); // Negation expect(value).not.toBe(expected);
3. Mocking
Mock Functions
// Create mock function const mockFn = jest.fn(); const mockFnWithReturn = jest.fn().mockReturnValue(42); const mockFnWithImpl = jest.fn((x) => x * 2); // Mock implementations mockFn.mockReturnValue(10); mockFn.mockReturnValueOnce(5); mockFn.mockResolvedValue({ data: 'async' }); mockFn.mockRejectedValue(new Error('failed')); mockFn.mockImplementation((x) => x * 2); // Assertions expect(mockFn).toHaveBeenCalled(); expect(mockFn).toHaveBeenCalledTimes(3); expect(mockFn).toHaveBeenCalledWith(arg1, arg2); expect(mockFn).toHaveBeenLastCalledWith(arg); expect(mockFn).toHaveBeenNthCalledWith(2, arg); expect(mockFn).toHaveReturnedWith(value);
Mock Modules
// Mock entire module jest.mock('./database'); // With implementation jest.mock('./api', () => ({ fetchUser: jest.fn().mockResolvedValue({ id: 1, name: 'John' }), updateUser: jest.fn().mockResolvedValue({ success: true }) })); // Partial mock jest.mock('./utils', () => ({ ...jest.requireActual('./utils'), formatDate: jest.fn().mockReturnValue('2024-01-01') })); // Manual mock (__mocks__/module.js) // Automatically used when jest.mock('./module') is called
Mock Classes
jest.mock('./UserService'); const UserService = require('./UserService'); // Mock instance methods UserService.prototype.getUser = jest.fn().mockResolvedValue({ id: 1 }); // Or mock the constructor UserService.mockImplementation(() => ({ getUser: jest.fn().mockResolvedValue({ id: 1 }), createUser: jest.fn().mockResolvedValue({ id: 2 }) }));
Spy on Methods
const obj = { method: () => 'original' }; // Spy without changing behavior const spy = jest.spyOn(obj, 'method'); obj.method(); expect(spy).toHaveBeenCalled(); // Spy with mock implementation jest.spyOn(obj, 'method').mockReturnValue('mocked'); expect(obj.method()).toBe('mocked'); // Restore original spy.mockRestore();
Mock Timers
beforeEach(() => { jest.useFakeTimers(); }); afterEach(() => { jest.useRealTimers(); }); it('calls callback after delay', () => { const callback = jest.fn(); setTimeout(callback, 1000); expect(callback).not.toHaveBeenCalled(); jest.advanceTimersByTime(1000); expect(callback).toHaveBeenCalled(); }); it('runs all timers', () => { const callback = jest.fn(); setTimeout(callback, 1000); setTimeout(callback, 2000); jest.runAllTimers(); expect(callback).toHaveBeenCalledTimes(2); });
4. Async Testing
// Promises it('resolves with data', async () => { const data = await fetchData(); expect(data).toEqual({ id: 1 }); }); // Or with resolves/rejects it('resolves with data', () => { return expect(fetchData()).resolves.toEqual({ id: 1 }); }); it('rejects with error', () => { return expect(failingFetch()).rejects.toThrow('Network error'); }); // Callbacks it('calls callback with data', (done) => { fetchDataCallback((error, data) => { try { expect(error).toBeNull(); expect(data).toEqual({ id: 1 }); done(); } catch (e) { done(e); } }); }); // Async with expect.assertions it('handles multiple assertions', async () => { expect.assertions(2); const data = await fetchData(); expect(data.id).toBe(1); expect(data.name).toBe('John'); });
5. Snapshot Testing
// Basic snapshot it('renders correctly', () => { const tree = renderer.create(<Button>Click</Button>).toJSON(); expect(tree).toMatchSnapshot(); }); // Inline snapshot it('returns user object', () => { const user = getUser(1); expect(user).toMatchInlineSnapshot(` Object { "id": 1, "name": "John", } `); }); // Custom serializer expect.addSnapshotSerializer({ test: (val) => val && val.testId, print: (val) => `TestID: ${val.testId}` }); // Update snapshots // Run: jest --updateSnapshot or jest -u
6. React Testing
import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; describe('LoginForm', () => { it('renders form fields', () => { render(<LoginForm />); expect(screen.getByLabelText(/email/i)).toBeInTheDocument(); expect(screen.getByLabelText(/password/i)).toBeInTheDocument(); expect(screen.getByRole('button', { name: /login/i })).toBeInTheDocument(); }); it('shows error for invalid email', async () => { const user = userEvent.setup(); render(<LoginForm />); await user.type(screen.getByLabelText(/email/i), 'invalid'); await user.click(screen.getByRole('button', { name: /login/i })); expect(screen.getByText(/invalid email/i)).toBeInTheDocument(); }); it('submits form with valid data', async () => { const onSubmit = jest.fn(); const user = userEvent.setup(); render(<LoginForm onSubmit={onSubmit} />); await user.type(screen.getByLabelText(/email/i), 'test@example.com'); await user.type(screen.getByLabelText(/password/i), 'password123'); await user.click(screen.getByRole('button', { name: /login/i })); await waitFor(() => { expect(onSubmit).toHaveBeenCalledWith({ email: 'test@example.com', password: 'password123' }); }); }); it('renders async content', async () => { render(<UserProfile userId={1} />); // Wait for loading to finish expect(screen.getByText(/loading/i)).toBeInTheDocument(); await waitFor(() => { expect(screen.getByText(/john doe/i)).toBeInTheDocument(); }); }); });
7. API/HTTP Testing
import axios from 'axios'; jest.mock('axios'); describe('API Service', () => { it('fetches users', async () => { const users = [{ id: 1, name: 'John' }]; axios.get.mockResolvedValue({ data: users }); const result = await fetchUsers(); expect(axios.get).toHaveBeenCalledWith('/api/users'); expect(result).toEqual(users); }); it('handles error', async () => { axios.get.mockRejectedValue(new Error('Network error')); await expect(fetchUsers()).rejects.toThrow('Network error'); }); }); // With MSW (Mock Service Worker) import { setupServer } from 'msw/node'; import { http, HttpResponse } from 'msw'; const server = setupServer( http.get('/api/users', () => { return HttpResponse.json([{ id: 1, name: 'John' }]); }) ); beforeAll(() => server.listen()); afterEach(() => server.resetHandlers()); afterAll(() => server.close()); it('fetches users', async () => { const users = await fetchUsers(); expect(users).toHaveLength(1); });
8. Coverage
# Generate coverage report jest --coverage # Coverage thresholds in jest.config.js coverageThreshold: { global: { branches: 80, functions: 80, lines: 80, statements: 80 }, './src/utils/': { branches: 100, statements: 100 } }
9. Custom Matchers
// jest.setup.js expect.extend({ 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 }; } } }); // Usage expect(100).toBeWithinRange(90, 110);
Best Practices
- Test behavior, not implementation - Focus on what, not how
- Use descriptive names - Clear test descriptions
- One assertion per test - When possible
- Arrange-Act-Assert - Structure tests clearly
- Mock external dependencies - Isolate units
- Use beforeEach for setup - DRY test code
- Clean up after tests - Prevent test pollution
- Run tests in watch mode - Faster feedback
- Maintain high coverage - But focus on quality
- Use snapshot tests wisely - For UI components