{"id":"43f3fd9d-c826-46bf-8cba-ebc663bb7e81","shortId":"jV88AK","kind":"skill","title":"testing-patterns","tagline":"Jest testing patterns, factory functions, mocking strategies, and TDD workflow. Use when writing unit tests, creating test factories, or following TDD red-green-refactor cycle.","description":"# Testing Patterns and Utilities\n\n## Testing Philosophy\n\n**Test-Driven Development (TDD):**\n- Write failing test FIRST\n- Implement minimal code to pass\n- Refactor after green\n- Never write production code without a failing test\n\n**Behavior-Driven Testing:**\n- Test behavior, not implementation\n- Focus on public APIs and business requirements\n- Avoid testing implementation details\n- Use descriptive test names that describe behavior\n\n**Factory Pattern:**\n- Create `getMockX(overrides?: Partial<X>)` functions\n- Provide sensible defaults\n- Allow overriding specific properties\n- Keep tests DRY and maintainable\n\n## Test Utilities\n\n### Custom Render Function\n\nCreate a custom render that wraps components with required providers:\n\n```typescript\n// src/utils/testUtils.tsx\nimport { render } from '@testing-library/react-native';\nimport { ThemeProvider } from './theme';\n\nexport const renderWithTheme = (ui: React.ReactElement) => {\n  return render(\n    <ThemeProvider>{ui}</ThemeProvider>\n  );\n};\n```\n\n**Usage:**\n```typescript\nimport { renderWithTheme } from 'utils/testUtils';\nimport { screen } from '@testing-library/react-native';\n\nit('should render component', () => {\n  renderWithTheme(<MyComponent />);\n  expect(screen.getByText('Hello')).toBeTruthy();\n});\n```\n\n## Factory Pattern\n\n### Component Props Factory\n\n```typescript\nimport { ComponentProps } from 'react';\n\nconst getMockMyComponentProps = (\n  overrides?: Partial<ComponentProps<typeof MyComponent>>\n) => {\n  return {\n    title: 'Default Title',\n    count: 0,\n    onPress: jest.fn(),\n    isLoading: false,\n    ...overrides,\n  };\n};\n\n// Usage in tests\nit('should render with custom title', () => {\n  const props = getMockMyComponentProps({ title: 'Custom Title' });\n  renderWithTheme(<MyComponent {...props} />);\n  expect(screen.getByText('Custom Title')).toBeTruthy();\n});\n```\n\n### Data Factory\n\n```typescript\ninterface User {\n  id: string;\n  name: string;\n  email: string;\n  role: 'admin' | 'user';\n}\n\nconst getMockUser = (overrides?: Partial<User>): User => {\n  return {\n    id: '123',\n    name: 'John Doe',\n    email: 'john@example.com',\n    role: 'user',\n    ...overrides,\n  };\n};\n\n// Usage\nit('should display admin badge for admin users', () => {\n  const user = getMockUser({ role: 'admin' });\n  renderWithTheme(<UserCard user={user} />);\n  expect(screen.getByText('Admin')).toBeTruthy();\n});\n```\n\n## Mocking Patterns\n\n### Mocking Modules\n\n```typescript\n// Mock entire module\njest.mock('utils/analytics');\n\n// Mock with factory function\njest.mock('utils/analytics', () => ({\n  Analytics: {\n    logEvent: jest.fn(),\n  },\n}));\n\n// Access mock in test\nconst mockLogEvent = jest.requireMock('utils/analytics').Analytics.logEvent;\n```\n\n### Mocking GraphQL Hooks\n\n```typescript\njest.mock('./GetItems.generated', () => ({\n  useGetItemsQuery: jest.fn(),\n}));\n\nconst mockUseGetItemsQuery = jest.requireMock(\n  './GetItems.generated'\n).useGetItemsQuery as jest.Mock;\n\n// In test\nmockUseGetItemsQuery.mockReturnValue({\n  data: { items: [] },\n  loading: false,\n  error: undefined,\n});\n```\n\n## Test Structure\n\n```typescript\ndescribe('ComponentName', () => {\n  beforeEach(() => {\n    jest.clearAllMocks();\n  });\n\n  describe('Rendering', () => {\n    it('should render component with default props', () => {});\n    it('should render loading state when loading', () => {});\n  });\n\n  describe('User interactions', () => {\n    it('should call onPress when button is clicked', async () => {});\n  });\n\n  describe('Edge cases', () => {\n    it('should handle empty data gracefully', () => {});\n  });\n});\n```\n\n## Query Patterns\n\n```typescript\n// Element must exist\nexpect(screen.getByText('Hello')).toBeTruthy();\n\n// Element should not exist\nexpect(screen.queryByText('Goodbye')).toBeNull();\n\n// Element appears asynchronously\nawait waitFor(() => {\n  expect(screen.findByText('Loaded')).toBeTruthy();\n});\n```\n\n## User Interaction Patterns\n\n```typescript\nimport { fireEvent, screen } from '@testing-library/react-native';\n\nit('should submit form on button click', async () => {\n  const onSubmit = jest.fn();\n  renderWithTheme(<LoginForm onSubmit={onSubmit} />);\n\n  fireEvent.changeText(screen.getByLabelText('Email'), 'user@example.com');\n  fireEvent.changeText(screen.getByLabelText('Password'), 'password123');\n  fireEvent.press(screen.getByTestId('login-button'));\n\n  await waitFor(() => {\n    expect(onSubmit).toHaveBeenCalled();\n  });\n});\n```\n\n## Anti-Patterns to Avoid\n\n### Testing Mock Behavior Instead of Real Behavior\n\n```typescript\n// Bad - testing the mock\nexpect(mockFetchData).toHaveBeenCalled();\n\n// Good - testing actual behavior\nexpect(screen.getByText('John Doe')).toBeTruthy();\n```\n\n### Not Using Factories\n\n```typescript\n// Bad - duplicated, inconsistent test data\nit('test 1', () => {\n  const user = { id: '1', name: 'John', email: 'john@test.com', role: 'user' };\n});\nit('test 2', () => {\n  const user = { id: '2', name: 'Jane', email: 'jane@test.com' }; // Missing role!\n});\n\n// Good - reusable factory\nconst user = getMockUser({ name: 'Custom Name' });\n```\n\n## Best Practices\n\n1. **Always use factory functions** for props and data\n2. **Test behavior, not implementation**\n3. **Use descriptive test names**\n4. **Organize with describe blocks**\n5. **Clear mocks between tests**\n6. **Keep tests focused** - one behavior per test\n\n## Running Tests\n\n```bash\n# Run all tests\nnpm test\n\n# Run with coverage\nnpm run test:coverage\n\n# Run specific file\nnpm test ComponentName.test.tsx\n```\n\n## Integration with Other Skills\n\n- **react-ui-patterns**: Test all UI states (loading, error, empty, success)\n- **systematic-debugging**: Write test that reproduces bug before fixing\n\n## When to Use\nThis skill is applicable to execute the workflow or actions described in the overview.\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.","tags":["testing","patterns","antigravity","awesome","skills","sickn33","agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding","ai-workflows"],"capabilities":["skill","source-sickn33","skill-testing-patterns","topic-agent-skills","topic-agentic-skills","topic-ai-agent-skills","topic-ai-agents","topic-ai-coding","topic-ai-workflows","topic-antigravity","topic-antigravity-skills","topic-claude-code","topic-claude-code-skills","topic-codex-cli","topic-codex-skills"],"categories":["antigravity-awesome-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/sickn33/antigravity-awesome-skills/testing-patterns","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add sickn33/antigravity-awesome-skills","source_repo":"https://github.com/sickn33/antigravity-awesome-skills","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 37911 github stars · SKILL.md body (6,050 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T18:51:53.580Z","embedding":null,"createdAt":"2026-04-18T20:38:19.039Z","updatedAt":"2026-05-18T18:51:53.580Z","lastSeenAt":"2026-05-18T18:51:53.580Z","tsv":"'/getitems.generated':298,304 '/react-native':129,154,399 '/theme':133 '0':184 '1':473,477,508 '123':234 '2':486,490,517 '3':522 '4':527 '5':532 '6':537 'access':284 'action':604 'actual':455 'admin':225,247,250,256,263 'allow':97 'alway':509 'analyt':281 'analytics.logevent':292 'anti':434 'anti-pattern':433 'api':72 'appear':380 'applic':598 'ask':642 'async':351,407 'asynchron':381 'avoid':76,437 'await':382,428 'bad':446,466 'badg':248 'bash':547 'beforeeach':322 'behavior':62,66,86,440,444,456,519,542 'behavior-driven':61 'best':506 'block':531 'boundari':650 'bug':589 'busi':74 'button':348,405,427 'call':345 'case':354 'clarif':644 'clear':533,617 'click':350,406 'code':47,56 'compon':117,158,166,329 'componentnam':321 'componentname.test.tsx':565 'componentprop':171,178 'const':135,174,199,227,252,288,301,408,474,487,500 'count':183 'coverag':555,559 'creat':19,89,111 'criteria':653 'custom':108,113,197,203,210,504 'cycl':29 'data':213,311,359,470,516 'debug':584 'default':96,181,331 'describ':85,320,324,340,352,530,605,621 'descript':81,524 'detail':79 'develop':39 'display':246 'doe':237,460 'dri':103 'driven':38,63 'duplic':467 'edg':353 'element':364,371,379 'email':222,238,417,480,493 'empti':358,580 'entir':271 'environ':633 'environment-specif':632 'error':315,579 'execut':600 'exist':366,374 'expect':160,208,261,367,375,384,430,450,457 'expert':638 'export':134 'factori':7,21,87,164,168,214,277,464,499,511 'fail':42,59 'fals':188,314 'file':562 'fireev':393 'fireevent.changetext':415,419 'fireevent.press':423 'first':44 'fix':591 'focus':69,540 'follow':23 'form':403 'function':8,93,110,278,512 'getmockmycomponentprop':175,201 'getmockus':228,254,502 'getmockx':90 'good':453,497 'goodby':377 'grace':360 'graphql':294 'green':27,52 'handl':357 'hello':162,369 'hook':295 'id':218,233,476,489 'implement':45,68,78,521 'import':123,130,144,148,170,392 'inconsist':468 'input':647 'instead':441 'integr':566 'interact':342,389 'interfac':216 'isload':187 'item':312 'jane':492 'jane@test.com':494 'jest':4 'jest.clearallmocks':323 'jest.fn':186,283,300,410 'jest.mock':273,279,297,307 'jest.requiremock':290,303 'john':236,459,479 'john@example.com':239 'john@test.com':481 'keep':101,538 'librari':128,153,398 'limit':609 'load':313,336,339,386,578 'logev':282 'login':426 'login-button':425 'loginform':412 'maintain':105 'match':618 'minim':46 'miss':495,655 'mock':9,265,267,270,275,285,293,439,449,534 'mockfetchdata':451 'mocklogev':289 'mockusegetitemsqueri':302 'mockusegetitemsquery.mockreturnvalue':310 'modul':268,272 'must':365 'mycompon':206 'name':83,220,235,478,491,503,505,526 'never':53 'npm':551,556,563 'one':541 'onpress':185,346 'onsubmit':409,413,414,431 'organ':528 'output':627 'overrid':91,98,176,189,229,242 'overview':608 'partial':92,177,230 'pass':49 'password':421 'password123':422 'pattern':3,6,31,88,165,266,362,390,435,573 'per':543 'permiss':648 'philosophi':35 'practic':507 'product':55 'prop':167,200,207,332,514 'properti':100 'provid':94,120 'public':71 'queri':361 'react':173,571 'react-ui-pattern':570 'react.reactelement':138 'real':443 'red':26 'red-green-refactor':25 'refactor':28,50 'render':109,114,124,140,157,195,325,328,335 'renderwiththem':136,145,159,205,257,411 'reproduc':588 'requir':75,119,646 'return':139,179,232 'reusabl':498 'review':639 'role':224,240,255,482,496 'run':545,548,553,557,560 'safeti':649 'scope':620 'screen':149,394 'screen.findbytext':385 'screen.getbylabeltext':416,420 'screen.getbytestid':424 'screen.getbytext':161,209,262,368,458 'screen.querybytext':376 'sensibl':95 'skill':569,596,612 'skill-testing-patterns' 'source-sickn33' 'specif':99,561,634 'src/utils/testutils.tsx':122 'state':337,577 'stop':640 'strategi':10 'string':219,221,223 'structur':318 'submit':402 'substitut':630 'success':581,652 'systemat':583 'systematic-debug':582 'task':616 'tdd':12,24,40 'test':2,5,18,20,30,34,37,43,60,64,65,77,82,102,106,127,152,192,287,309,317,397,438,447,454,469,472,485,518,525,536,539,544,546,550,552,558,564,574,586,636 'test-driven':36 'testing-librari':126,151,396 'testing-pattern':1 'themeprovid':131 'titl':180,182,198,202,204,211 'tobenul':378 'tobetruthi':163,212,264,370,387,461 'tohavebeencal':432,452 'topic-agent-skills' 'topic-agentic-skills' 'topic-ai-agent-skills' 'topic-ai-agents' 'topic-ai-coding' 'topic-ai-workflows' 'topic-antigravity' 'topic-antigravity-skills' 'topic-claude-code' 'topic-claude-code-skills' 'topic-codex-cli' 'topic-codex-skills' 'treat':625 'typescript':121,143,169,215,269,296,319,363,391,445,465 'ui':137,141,572,576 'undefin':316 'unit':17 'usag':142,190,243 'use':14,80,463,510,523,594,610 'usegetitemsqueri':299,305 'user':217,226,231,241,251,253,259,260,341,388,475,483,488,501 'user@example.com':418 'usercard':258 'util':33,107 'utils/analytics':274,280,291 'utils/testutils':147 'valid':635 'waitfor':383,429 'without':57 'workflow':13,602 'wrap':116 'write':16,41,54,585","prices":[{"id":"a5c5ffa3-86c4-4ca3-a6d3-49436daddca4","listingId":"43f3fd9d-c826-46bf-8cba-ebc663bb7e81","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"sickn33","category":"antigravity-awesome-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T20:38:19.039Z"}],"sources":[{"listingId":"43f3fd9d-c826-46bf-8cba-ebc663bb7e81","source":"github","sourceId":"sickn33/antigravity-awesome-skills/testing-patterns","sourceUrl":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/testing-patterns","isPrimary":false,"firstSeenAt":"2026-04-18T21:46:12.298Z","lastSeenAt":"2026-05-18T18:51:53.580Z"},{"listingId":"43f3fd9d-c826-46bf-8cba-ebc663bb7e81","source":"skills_sh","sourceId":"sickn33/antigravity-awesome-skills/testing-patterns","sourceUrl":"https://skills.sh/sickn33/antigravity-awesome-skills/testing-patterns","isPrimary":true,"firstSeenAt":"2026-04-18T20:38:19.039Z","lastSeenAt":"2026-05-07T22:40:47.373Z"}],"details":{"listingId":"43f3fd9d-c826-46bf-8cba-ebc663bb7e81","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"sickn33","slug":"testing-patterns","github":{"repo":"sickn33/antigravity-awesome-skills","stars":37911,"topics":["agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding","ai-workflows","antigravity","antigravity-skills","claude-code","claude-code-skills","codex-cli","codex-skills","cursor","cursor-skills","developer-tools","gemini-cli","gemini-skills","kiro","mcp","skill-library"],"license":"mit","html_url":"https://github.com/sickn33/antigravity-awesome-skills","pushed_at":"2026-05-18T08:24:49Z","description":"Installable GitHub library of 1,400+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and more. Includes installer CLI, bundles, workflows, and official/community skill collections.","skill_md_sha":"0708cef714a3f96ac673236772c8f632e3ebf969","skill_md_path":"skills/testing-patterns/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/testing-patterns"},"layout":"multi","source":"github","category":"antigravity-awesome-skills","frontmatter":{"name":"testing-patterns","description":"Jest testing patterns, factory functions, mocking strategies, and TDD workflow. Use when writing unit tests, creating test factories, or following TDD red-green-refactor cycle."},"skills_sh_url":"https://skills.sh/sickn33/antigravity-awesome-skills/testing-patterns"},"updatedAt":"2026-05-18T18:51:53.580Z"}}