Jest Testing Skill Core Patterns Basic Test Matchers Mocking Async Testing React Component Testing (Testing Library) Snapshot Testing Anti-Patterns | Bad | Good | Why | |-----|------|-----| | | | Better errors | | No on async | Always | Swallows failures | | Snapshot everything | Snapshot UI, assert logic | Snapshot fatigue | Quick Reference | Task | Command | |------|---------| | Run all | | | Watch | | | Coverage | | | Update snapshots | | | Run file | | | Single test | | Deep Patterns For production-grade patterns, see : | Section | What's Inside | |---------|--------------| | §1 Productio…

: '\u003crootDir>/src/$1',\n '\\\\.(css|less|scss)

Jest Testing Skill Core Patterns Basic Test Matchers Mocking Async Testing React Component Testing (Testing Library) Snapshot Testing Anti-Patterns | Bad | Good | Why | |-----|------|-----| | | | Better errors | | No on async | Always | Swallows failures | | Snapshot everything | Snapshot UI, assert logic | Snapshot fatigue | Quick Reference | Task | Command | |------|---------| | Run all | | | Watch | | | Coverage | | | Update snapshots | | | Run file | | | Single test | | Deep Patterns For production-grade patterns, see : | Section | What's Inside | |---------|--------------| | §1 Productio…

: 'identity-obj-proxy',\n '\\\\.(jpg|jpeg|png|svg)

Jest Testing Skill Core Patterns Basic Test Matchers Mocking Async Testing React Component Testing (Testing Library) Snapshot Testing Anti-Patterns | Bad | Good | Why | |-----|------|-----| | | | Better errors | | No on async | Always | Swallows failures | | Snapshot everything | Snapshot UI, assert logic | Snapshot fatigue | Quick Reference | Task | Command | |------|---------| | Run all | | | Watch | | | Coverage | | | Update snapshots | | | Run file | | | Single test | | Deep Patterns For production-grade patterns, see : | Section | What's Inside | |---------|--------------| | §1 Productio…

: '\u003crootDir>/__mocks__/fileMock.js'\n },\n setupFilesAfterSetup: ['\u003crootDir>/jest.setup.js'],\n transform: { '^.+\\\\.(ts|tsx)

Jest Testing Skill Core Patterns Basic Test Matchers Mocking Async Testing React Component Testing (Testing Library) Snapshot Testing Anti-Patterns | Bad | Good | Why | |-----|------|-----| | | | Better errors | | No on async | Always | Swallows failures | | Snapshot everything | Snapshot UI, assert logic | Snapshot fatigue | Quick Reference | Task | Command | |------|---------| | Run all | | | Watch | | | Coverage | | | Update snapshots | | | Run file | | | Single test | | Deep Patterns For production-grade patterns, see : | Section | What's Inside | |---------|--------------| | §1 Productio…

: 'ts-jest' },\n testMatch: ['**/__tests__/**/*.(spec|test).[jt]s?(x)'],\n watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname']\n};\n```\n\n## Anti-Patterns\n\n- ❌ `test('works', () => { myFunction(); })` — assertion-free tests prove nothing\n- ❌ Testing implementation details (internal state, private methods)\n- ❌ Snapshot overuse — large snapshots become meaningless; prefer targeted assertions\n- ❌ `jest.mock()` at file level when only one test needs it — use `jest.spyOn` per-test instead\n- ❌ `setTimeout` in tests — use `jest.useFakeTimers()` and `advanceTimersByTime`\n- ❌ Shared mutable state between tests — always reset in `beforeEach`\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7256,"content_sha256":"8e445ef801adff52bb0f4416929aaf155ff1b2a4d07e09295eb2d53074a78e69"},{"filename":"reference/playbook.md","content":"# Jest — Advanced Implementation Playbook\n\n## §1 — Production Config\n\n```javascript\n// jest.config.js\nmodule.exports = {\n testEnvironment: 'node',\n roots: ['\u003crootDir>/src', '\u003crootDir>/tests'],\n testMatch: ['**/*.test.{js,ts}'],\n transform: { '^.+\\\\.tsx?

Jest Testing Skill Core Patterns Basic Test Matchers Mocking Async Testing React Component Testing (Testing Library) Snapshot Testing Anti-Patterns | Bad | Good | Why | |-----|------|-----| | | | Better errors | | No on async | Always | Swallows failures | | Snapshot everything | Snapshot UI, assert logic | Snapshot fatigue | Quick Reference | Task | Command | |------|---------| | Run all | | | Watch | | | Coverage | | | Update snapshots | | | Run file | | | Single test | | Deep Patterns For production-grade patterns, see : | Section | What's Inside | |---------|--------------| | §1 Productio…

: 'ts-jest' },\n collectCoverage: true,\n coverageDirectory: 'coverage',\n coverageReporters: ['text', 'lcov', 'json-summary'],\n coverageThreshold: { global: { branches: 80, functions: 80, lines: 80, statements: 80 } },\n coveragePathIgnorePatterns: ['/node_modules/', '/tests/', '/dist/'],\n setupFilesAfterSetup: ['\u003crootDir>/tests/setup.ts'],\n moduleNameMapper: {\n '^@/(.*)

Jest Testing Skill Core Patterns Basic Test Matchers Mocking Async Testing React Component Testing (Testing Library) Snapshot Testing Anti-Patterns | Bad | Good | Why | |-----|------|-----| | | | Better errors | | No on async | Always | Swallows failures | | Snapshot everything | Snapshot UI, assert logic | Snapshot fatigue | Quick Reference | Task | Command | |------|---------| | Run all | | | Watch | | | Coverage | | | Update snapshots | | | Run file | | | Single test | | Deep Patterns For production-grade patterns, see : | Section | What's Inside | |---------|--------------| | §1 Productio…

: '\u003crootDir>/src/$1',\n '\\\\.(css|less|scss)

Jest Testing Skill Core Patterns Basic Test Matchers Mocking Async Testing React Component Testing (Testing Library) Snapshot Testing Anti-Patterns | Bad | Good | Why | |-----|------|-----| | | | Better errors | | No on async | Always | Swallows failures | | Snapshot everything | Snapshot UI, assert logic | Snapshot fatigue | Quick Reference | Task | Command | |------|---------| | Run all | | | Watch | | | Coverage | | | Update snapshots | | | Run file | | | Single test | | Deep Patterns For production-grade patterns, see : | Section | What's Inside | |---------|--------------| | §1 Productio…

: 'identity-obj-proxy',\n },\n verbose: true,\n testTimeout: 10000,\n maxWorkers: '50%', // Use half CPU cores\n};\n```\n\n```javascript\n// jest.config.js — for React projects\nmodule.exports = {\n testEnvironment: 'jsdom',\n setupFilesAfterSetup: ['@testing-library/jest-dom', '\u003crootDir>/tests/setup.ts'],\n transform: { '^.+\\\\.(t|j)sx?

Jest Testing Skill Core Patterns Basic Test Matchers Mocking Async Testing React Component Testing (Testing Library) Snapshot Testing Anti-Patterns | Bad | Good | Why | |-----|------|-----| | | | Better errors | | No on async | Always | Swallows failures | | Snapshot everything | Snapshot UI, assert logic | Snapshot fatigue | Quick Reference | Task | Command | |------|---------| | Run all | | | Watch | | | Coverage | | | Update snapshots | | | Run file | | | Single test | | Deep Patterns For production-grade patterns, see : | Section | What's Inside | |---------|--------------| | §1 Productio…

: ['@swc/jest'] }, // Faster than ts-jest\n moduleNameMapper: {\n '\\\\.(css|scss)

Jest Testing Skill Core Patterns Basic Test Matchers Mocking Async Testing React Component Testing (Testing Library) Snapshot Testing Anti-Patterns | Bad | Good | Why | |-----|------|-----| | | | Better errors | | No on async | Always | Swallows failures | | Snapshot everything | Snapshot UI, assert logic | Snapshot fatigue | Quick Reference | Task | Command | |------|---------| | Run all | | | Watch | | | Coverage | | | Update snapshots | | | Run file | | | Single test | | Deep Patterns For production-grade patterns, see : | Section | What's Inside | |---------|--------------| | §1 Productio…

: 'identity-obj-proxy',\n '\\\\.(jpg|png|svg)

Jest Testing Skill Core Patterns Basic Test Matchers Mocking Async Testing React Component Testing (Testing Library) Snapshot Testing Anti-Patterns | Bad | Good | Why | |-----|------|-----| | | | Better errors | | No on async | Always | Swallows failures | | Snapshot everything | Snapshot UI, assert logic | Snapshot fatigue | Quick Reference | Task | Command | |------|---------| | Run all | | | Watch | | | Coverage | | | Update snapshots | | | Run file | | | Single test | | Deep Patterns For production-grade patterns, see : | Section | What's Inside | |---------|--------------| | §1 Productio…

: '\u003crootDir>/tests/__mocks__/fileMock.js',\n },\n};\n```\n\n## §2 — Mocking Deep Dive\n\n```javascript\n// Full module mock\njest.mock('./database', () => ({\n query: jest.fn(),\n connect: jest.fn().mockResolvedValue(true),\n}));\n\n// Partial mock (keep real implementations for some functions)\njest.mock('./utils', () => ({\n ...jest.requireActual('./utils'),\n fetchData: jest.fn(),\n}));\n\n// Manual mock — __mocks__/axios.js\nmodule.exports = {\n get: jest.fn(() => Promise.resolve({ data: {} })),\n post: jest.fn(() => Promise.resolve({ data: {} })),\n create: jest.fn(function() { return this; }),\n};\n\n// Spy with chained responses\nconst spy = jest.spyOn(userService, 'findById')\n .mockResolvedValueOnce({ id: 1, name: 'Alice' })\n .mockResolvedValueOnce(null);\n\nexpect(await userService.findById(1)).toEqual({ id: 1, name: 'Alice' });\nexpect(await userService.findById(999)).toBeNull();\nspy.mockRestore();\n\n// Timer mocking\njest.useFakeTimers();\nconst callback = jest.fn();\nsetTimeout(callback, 5000);\njest.advanceTimersByTime(5000);\nexpect(callback).toHaveBeenCalledTimes(1);\njest.useRealTimers();\n\n// Mock Date\njest.useFakeTimers({ now: new Date('2024-12-25T00:00:00Z') });\nexpect(new Date().toISOString()).toBe('2024-12-25T00:00:00.000Z');\n\n// Mock environment variables\nconst originalEnv = process.env;\nbeforeEach(() => { process.env = { ...originalEnv, API_KEY: 'test-key' }; });\nafterEach(() => { process.env = originalEnv; });\n\n// Mock console\nconst consoleSpy = jest.spyOn(console, 'error').mockImplementation();\nafterEach(() => consoleSpy.mockRestore());\n```\n\n## §3 — Async Patterns\n\n```javascript\n// Async/await\ntest('fetches user', async () => {\n const user = await fetchUser(1);\n expect(user.name).toBe('Alice');\n});\n\n// Rejection\ntest('rejects on not found', async () => {\n await expect(fetchUser(999)).rejects.toThrow('Not found');\n});\n\n// Resolves/rejects matchers\ntest('resolves with data', () => {\n return expect(fetchUser(1)).resolves.toMatchObject({ name: 'Alice' });\n});\n\n// Testing event emitters\ntest('emits data event', (done) => {\n const emitter = createDataStream();\n emitter.on('data', (data) => {\n expect(data).toBeDefined();\n done();\n });\n emitter.start();\n});\n\n// Testing streams / callbacks with timeout\ntest('stream completes', async () => {\n const result = await new Promise((resolve, reject) => {\n const chunks = [];\n stream.on('data', chunk => chunks.push(chunk));\n stream.on('end', () => resolve(Buffer.concat(chunks)));\n stream.on('error', reject);\n });\n expect(result.length).toBeGreaterThan(0);\n});\n```\n\n## §4 — Table-Driven Tests (test.each)\n\n```javascript\n// Array format\ntest.each([\n ['[email protected]', 'pass123', true],\n ['[email protected]', 'wrong', false],\n ['', '', false],\n ['invalid-email', 'pass', false],\n])('login(%s, %s) => %s', async (email, password, expected) => {\n const result = await authService.login(email, password);\n expect(result.success).toBe(expected);\n});\n\n// Tagged template literal\ntest.each`\n input | expected\n ${1} | ${'1'}\n ${null} | ${''}\n ${undefined} | ${''}\n ${'hello'} | ${'hello'}\n`('toString($input) => $expected', ({ input, expected }) => {\n expect(toString(input)).toBe(expected);\n});\n\n// describe.each for grouped tests\ndescribe.each([\n { role: 'admin', canDelete: true, canEdit: true },\n { role: 'editor', canDelete: false, canEdit: true },\n { role: 'viewer', canDelete: false, canEdit: false },\n])('$role permissions', ({ role, canDelete, canEdit }) => {\n test(`canDelete is ${canDelete}`, () => {\n expect(getPermissions(role).canDelete).toBe(canDelete);\n });\n test(`canEdit is ${canEdit}`, () => {\n expect(getPermissions(role).canEdit).toBe(canEdit);\n });\n});\n```\n\n## §5 — Custom Matchers\n\n```javascript\n// tests/setup.ts\nexpect.extend({\n toBeWithinRange(received, floor, ceiling) {\n const pass = received >= floor && received \u003c= ceiling;\n return {\n message: () => `expected ${received} to be within [${floor}, ${ceiling}]`,\n pass,\n };\n },\n toBeValidEmail(received) {\n const pass = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(received);\n return {\n message: () => `expected ${received} to be a valid email`,\n pass,\n };\n },\n toContainObject(received, argument) {\n const pass = received.some(item =>\n Object.keys(argument).every(key => item[key] === argument[key])\n );\n return {\n message: () => `expected array to contain object ${JSON.stringify(argument)}`,\n pass,\n };\n },\n});\n\n// TypeScript declaration\ndeclare global {\n namespace jest {\n interface Matchers\u003cR> {\n toBeWithinRange(floor: number, ceiling: number): R;\n toBeValidEmail(): R;\n toContainObject(obj: Record\u003cstring, unknown>): R;\n }\n }\n}\n\n// Usage\nexpect(response.status).toBeWithinRange(200, 299);\nexpect(user.email).toBeValidEmail();\nexpect(users).toContainObject({ name: 'Alice', role: 'admin' });\n```\n\n## §6 — React Testing (Testing Library)\n\n```javascript\nimport { render, screen, fireEvent, waitFor, within } from '@testing-library/react';\nimport userEvent from '@testing-library/user-event';\n\ntest('submits login form', async () => {\n const user = userEvent.setup();\n const onSubmit = jest.fn();\n render(\u003cLoginForm onSubmit={onSubmit} />);\n\n await user.type(screen.getByLabelText('Email'), '[email protected]');\n await user.type(screen.getByLabelText('Password'), 'pass');\n await user.click(screen.getByRole('button', { name: 'Sign In' }));\n\n await waitFor(() => {\n expect(onSubmit).toHaveBeenCalledWith({ email: '[email protected]', password: 'pass' });\n });\n});\n\ntest('shows validation error', async () => {\n const user = userEvent.setup();\n render(\u003cLoginForm onSubmit={jest.fn()} />);\n await user.click(screen.getByRole('button', { name: 'Sign In' }));\n expect(screen.getByText('Email is required')).toBeInTheDocument();\n});\n\n// Testing custom hooks\nimport { renderHook, act } from '@testing-library/react';\nimport { useCounter } from './useCounter';\n\ntest('useCounter increments', () => {\n const { result } = renderHook(() => useCounter(0));\n act(() => { result.current.increment(); });\n expect(result.current.count).toBe(1);\n});\n\n// Testing with context providers\nconst AllProviders = ({ children }) => (\n \u003cThemeProvider theme=\"dark\">\n \u003cAuthProvider user={mockUser}>\n {children}\n \u003c/AuthProvider>\n \u003c/ThemeProvider>\n);\n\ntest('renders with providers', () => {\n render(\u003cDashboard />, { wrapper: AllProviders });\n expect(screen.getByText('Welcome, Alice')).toBeInTheDocument();\n});\n```\n\n## §7 — Snapshot Testing\n\n```javascript\nimport renderer from 'react-test-renderer';\n\ntest('Header matches snapshot', () => {\n const tree = renderer.create(\u003cHeader title=\"Hello\" user={mockUser} />).toJSON();\n expect(tree).toMatchSnapshot();\n});\n\n// Inline snapshot (no file)\ntest('formats date', () => {\n expect(formatDate('2024-01-15')).toMatchInlineSnapshot(`\"January 15, 2024\"`);\n});\n\n// Property matchers (ignore dynamic fields)\ntest('user snapshot with dynamic id', () => {\n expect(createUser('Alice')).toMatchSnapshot({\n id: expect.any(String),\n createdAt: expect.any(Date),\n });\n});\n\n// Update: jest --updateSnapshot\n```\n\n## §8 — Testing API Services\n\n```javascript\nimport axios from 'axios';\nimport { UserService } from './UserService';\n\njest.mock('axios');\nconst mockedAxios = axios as jest.Mocked\u003ctypeof axios>;\n\ndescribe('UserService', () => {\n const service = new UserService();\n\n afterEach(() => jest.clearAllMocks());\n\n test('getUser returns user data', async () => {\n mockedAxios.get.mockResolvedValue({ data: { id: 1, name: 'Alice' } });\n const user = await service.getUser(1);\n expect(user).toEqual({ id: 1, name: 'Alice' });\n expect(mockedAxios.get).toHaveBeenCalledWith('/api/users/1');\n });\n\n test('getUser throws on 404', async () => {\n mockedAxios.get.mockRejectedValue({ response: { status: 404 } });\n await expect(service.getUser(999)).rejects.toThrow('User not found');\n });\n\n test('createUser sends POST', async () => {\n mockedAxios.post.mockResolvedValue({ data: { id: 2, name: 'Bob' } });\n const user = await service.createUser({ name: 'Bob' });\n expect(user.id).toBe(2);\n expect(mockedAxios.post).toHaveBeenCalledWith('/api/users', { name: 'Bob' });\n });\n});\n```\n\n## §9 — Global Setup/Teardown & Projects\n\n```javascript\n// jest.config.js — multi-project setup\nmodule.exports = {\n projects: [\n {\n displayName: 'unit',\n testMatch: ['\u003crootDir>/tests/unit/**/*.test.ts'],\n testEnvironment: 'node',\n },\n {\n displayName: 'integration',\n testMatch: ['\u003crootDir>/tests/integration/**/*.test.ts'],\n testEnvironment: 'node',\n globalSetup: '\u003crootDir>/tests/integration/globalSetup.ts',\n globalTeardown: '\u003crootDir>/tests/integration/globalTeardown.ts',\n },\n {\n displayName: 'react',\n testMatch: ['\u003crootDir>/src/**/*.test.tsx'],\n testEnvironment: 'jsdom',\n setupFilesAfterSetup: ['@testing-library/jest-dom'],\n },\n ],\n};\n\n// tests/integration/globalSetup.ts\nexport default async function globalSetup() {\n // Start test database, seed data, etc.\n process.env.DATABASE_URL = 'postgresql://localhost:5432/test';\n}\n\n// tests/integration/globalTeardown.ts\nexport default async function globalTeardown() {\n // Clean up test database\n}\n```\n\n## §10 — CI/CD Integration\n\n```yaml\n# GitHub Actions\nname: Tests\non: [push, pull_request]\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with: { node-version: 20 }\n - run: npm ci\n - run: npm test -- --ci --coverage --maxWorkers=2\n - name: Upload coverage\n uses: codecov/codecov-action@v4\n with:\n files: coverage/lcov.info\n - name: Check thresholds\n run: |\n COVERAGE=$(cat coverage/coverage-summary.json | jq '.total.lines.pct')\n if (( $(echo \"$COVERAGE \u003c 80\" | bc -l) )); then\n echo \"Coverage $COVERAGE% is below 80%\"\n exit 1\n fi\n```\n\n## §11 — Debugging Quick-Reference\n\n| Problem | Cause | Fix |\n|---------|-------|-----|\n| Mock not working | Mock path doesn't match import | Verify `jest.mock()` path matches exactly |\n| Async test timeout | Unresolved promise or missing await | Add `await`, increase `testTimeout` |\n| State leaking between tests | Shared mutable state | Use `beforeEach` + `jest.clearAllMocks()` |\n| `act()` warnings | State update not wrapped | Use `waitFor()` or `await act(async () => ...)` |\n| Snapshot too large | Entire component tree serialized | Use `toMatchInlineSnapshot()` or target subcomponents |\n| ESM imports fail | node_modules not transformed | Add package to `transformIgnorePatterns` |\n| `Cannot find module` | Path alias not configured | Add to `moduleNameMapper` in config |\n| Slow test suite | Too many workers / no cache | Use `--maxWorkers=50%`, enable `cacheDirectory` |\n| `ReferenceError: fetch` | Node \u003c 18 or jsdom env | Use `undici` or mock `global.fetch` |\n| Mock doesn't reset | Missing cleanup | Add `jest.restoreAllMocks()` in `afterEach` |\n\n## §12 — Best Practices Checklist\n\n- ✅ Use `beforeEach` + `jest.clearAllMocks()` to prevent leaks\n- ✅ Mock external dependencies, never internal logic\n- ✅ Use `toMatchObject` for partial object matching\n- ✅ Use `expect.any(Type)` and `expect.stringContaining()` for flexible assertions\n- ✅ Use `--watch` during development for fast feedback\n- ✅ Set coverage thresholds in config as quality gates\n- ✅ Use `jest.spyOn` over manual mocks when possible\n- ✅ Test behavior, not implementation details\n- ✅ Use `test.each` for parameterized/table-driven tests\n- ✅ Use `userEvent` over `fireEvent` for realistic user interactions\n- ✅ Use `renderHook` for testing custom React hooks\n- ✅ Use multi-project config to separate unit/integration/component\n- ✅ Use `@swc/jest` instead of `ts-jest` for 2-5x faster transforms\n- ✅ Use `--ci` flag in CI (disables interactive watch mode)\n- ✅ Structure: `tests/unit/`, `tests/integration/`, `src/**/*.test.tsx`\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":13245,"content_sha256":"49f26fdd9e9bd1e3dd3734c4534343b8ae6699b170cb5f4d3c2f7b9e09624aeb"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Jest Testing Skill","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Core Patterns","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Basic Test","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"javascript"},"content":[{"text":"describe('Calculator', () => {\n let calc;\n beforeEach(() => { calc = new Calculator(); });\n\n test('adds two numbers', () => {\n expect(calc.add(2, 3)).toBe(5);\n });\n\n test('throws on division by zero', () => {\n expect(() => calc.divide(10, 0)).toThrow('Division by zero');\n });\n});","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Matchers","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"javascript"},"content":[{"text":"expect(value).toBe(exact); // === strict\nexpect(value).toEqual(object); // deep equality\nexpect(value).toBeTruthy();\nexpect(value).toBeNull();\nexpect(value).toBeGreaterThan(3);\nexpect(value).toBeCloseTo(0.3, 5);\nexpect(str).toMatch(/regex/);\nexpect(arr).toContain(item);\nexpect(arr).toHaveLength(3);\nexpect(obj).toHaveProperty('name');\nexpect(obj).toMatchObject({ name: 'Alice' });\nexpect(() => fn()).toThrow(CustomError);","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Mocking","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"javascript"},"content":[{"text":"// Mock function\nconst mockFn = jest.fn();\nmockFn.mockReturnValue(42);\nmockFn.mockResolvedValue({ data: 'test' });\nexpect(mockFn).toHaveBeenCalledWith('arg1');\nexpect(mockFn).toHaveBeenCalledTimes(1);\n\n// Mock module\njest.mock('./database');\nconst db = require('./database');\ndb.getUser.mockResolvedValue({ name: 'Alice' });\n\n// Mock with implementation\njest.mock('./api', () => ({\n fetchUsers: jest.fn().mockResolvedValue([{ name: 'Alice' }]),\n}));\n\n// Spy\nconst spy = jest.spyOn(console, 'log').mockImplementation();\nexpect(spy).toHaveBeenCalledWith('expected');\nspy.mockRestore();\n\n// Fake timers\njest.useFakeTimers();\njest.advanceTimersByTime(1000);\njest.useRealTimers();","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Async Testing","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"javascript"},"content":[{"text":"test('fetches users', async () => {\n const users = await fetchUsers();\n expect(users).toHaveLength(3);\n});\n\ntest('resolves with data', () => {\n return expect(fetchData()).resolves.toEqual({ data: 'value' });\n});\n\ntest('rejects with error', () => {\n return expect(fetchBadData()).rejects.toThrow('not found');\n});","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"React Component Testing (Testing Library)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"javascript"},"content":[{"text":"import { render, screen, fireEvent, waitFor } from '@testing-library/react';\nimport '@testing-library/jest-dom';\nimport LoginForm from './LoginForm';\n\ntest('submits login form', async () => {\n const onSubmit = jest.fn();\n render(\u003cLoginForm onSubmit={onSubmit} />);\n\n fireEvent.change(screen.getByLabelText('Email'), {\n target: { value: '[email protected]' },\n });\n fireEvent.change(screen.getByLabelText('Password'), {\n target: { value: 'password123' },\n });\n fireEvent.click(screen.getByRole('button', { name: /login/i }));\n\n await waitFor(() => {\n expect(onSubmit).toHaveBeenCalledWith({\n email: '[email protected]', password: 'password123',\n });\n });\n});","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Snapshot Testing","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"javascript"},"content":[{"text":"test('renders correctly', () => {\n const tree = renderer.create(\u003cButton label=\"Click\" />).toJSON();\n expect(tree).toMatchSnapshot();\n});\n// Update: jest --updateSnapshot","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Anti-Patterns","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Bad","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Good","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Why","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"expect(x === y).toBe(true)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"expect(x).toBe(y)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Better errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No ","type":"text"},{"text":"await","type":"text","marks":[{"type":"code_inline"}]},{"text":" on async","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Always ","type":"text"},{"text":"await","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Swallows failures","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Snapshot everything","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Snapshot UI, assert logic","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Snapshot fatigue","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Reference","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Task","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Command","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Run all","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"npx jest","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Watch","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"npx jest --watch","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Coverage","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"npx jest --coverage","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Update snapshots","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"npx jest --updateSnapshot","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Run file","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"npx jest tests/calc.test.js","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Single test","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"test.only('name', () => {})","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Deep Patterns","type":"text"}]},{"type":"paragraph","content":[{"text":"For production-grade patterns, see ","type":"text"},{"text":"reference/playbook.md","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Section","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"What's Inside","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§1 Production Config","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Node + React configs, path aliases, coverage thresholds","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§2 Mocking Deep Dive","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Module/partial/manual mocks, spies, timers, env vars","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§3 Async Patterns","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Promises, rejections, event emitters, streams","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§4 test.each","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Array, tagged template, describe.each for table-driven tests","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§5 Custom Matchers","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"toBeWithinRange, toBeValidEmail, TypeScript declarations","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§6 React Testing Library","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"userEvent, hooks, context providers","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§7 Snapshot Testing","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Component, inline, property matchers","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§8 API Service Testing","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mocked axios, CRUD patterns, error handling","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§9 Global Setup","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Multi-project config, DB setup/teardown","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§10 CI/CD","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"GitHub Actions with coverage gates","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§11 Debugging Table","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"10 common problems with fixes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"§12 Best Practices","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"15-item production checklist","type":"text"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"jest-skill","author":"@skillopedia","source":{"stars":302,"repo_name":"agent-skills","origin_url":"https://github.com/lambdatest/agent-skills/blob/HEAD/jest-skill/SKILL.md","repo_owner":"lambdatest","body_sha256":"4fdb192bea55690997979aa44ed9b289f1aa0d5ab70f401de7bb9ea5d5f0c2fd","cluster_key":"4dba199183495c12947dd35eea7fc266f22fec2606d4ad50e1d41a771532e307","clean_bundle":{"format":"clean-skill-bundle-v1","source":"lambdatest/agent-skills/jest-skill/SKILL.md","attachments":[{"id":"68ff34fb-961b-55ec-8e86-02d11dda8511","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/68ff34fb-961b-55ec-8e86-02d11dda8511/attachment.md","path":"reference/advanced-patterns.md","size":7256,"sha256":"8e445ef801adff52bb0f4416929aaf155ff1b2a4d07e09295eb2d53074a78e69","contentType":"text/markdown; charset=utf-8"},{"id":"ad16f03b-9ae8-587b-8168-a8b465c8d649","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ad16f03b-9ae8-587b-8168-a8b465c8d649/attachment.md","path":"reference/playbook.md","size":13245,"sha256":"49f26fdd9e9bd1e3dd3734c4534343b8ae6699b170cb5f4d3c2f7b9e09624aeb","contentType":"text/markdown; charset=utf-8"}],"bundle_sha256":"592209e949047d10131f5d7befa61bff00737f7d79127cc9dde89a5c11ae7463","attachment_count":2,"text_attachments":2,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"jest-skill/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"testing-qa","category_label":"Testing"},"exact_dupes_collapsed_into_this":0},"license":"MIT","version":"v1","category":"testing-qa","metadata":{"author":"TestMu AI","version":"1.0"},"languages":["JavaScript","TypeScript"],"import_tag":"clean-skills-v1","description":"Generates Jest unit and integration tests in JavaScript or TypeScript. Covers mocking, snapshots, async testing, and React component testing. Use when user mentions \"Jest\", \"describe/it/expect\", \"jest.mock\", \"toMatchSnapshot\". Triggers on: \"Jest\", \"expect().toBe()\", \"jest.mock\", \"snapshot test\", \"JS test\", \"React test\".\n"}},"renderedAt":1782987539262}

Jest Testing Skill Core Patterns Basic Test Matchers Mocking Async Testing React Component Testing (Testing Library) Snapshot Testing Anti-Patterns | Bad | Good | Why | |-----|------|-----| | | | Better errors | | No on async | Always | Swallows failures | | Snapshot everything | Snapshot UI, assert logic | Snapshot fatigue | Quick Reference | Task | Command | |------|---------| | Run all | | | Watch | | | Coverage | | | Update snapshots | | | Run file | | | Single test | | Deep Patterns For production-grade patterns, see : | Section | What's Inside | |---------|--------------| | §1 Productio…