{"id":"43f3fd9d-c826-46bf-8cba-ebc663bb7e81","shortId":"jV88AK","kind":"skill","title":"Testing Patterns","tagline":"Antigravity Awesome Skills skill by Sickn33","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"],"capabilities":["skill","source-sickn33","category-antigravity-awesome-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":{"install_from":"skills.sh"}},"qualityScore":"0.300","qualityRationale":"deterministic score 0.30 from registry signals: · indexed on skills.sh · published under sickn33/antigravity-awesome-skills","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:v1","enrichmentVersion":1,"enrichedAt":"2026-04-25T07:40:49.555Z","embedding":null,"createdAt":"2026-04-18T20:38:19.039Z","updatedAt":"2026-04-25T07:40:49.555Z","lastSeenAt":"2026-04-25T07:40:49.555Z","tsv":"'/getitems.generated':277,283 '/react-native':108,133,378 '/theme':112 '0':163 '1':452,456,487 '123':213 '2':465,469,496 '3':501 '4':506 '5':511 '6':516 'access':263 'action':583 'actual':434 'admin':204,226,229,235,242 'allow':76 'alway':488 'analyt':260 'analytics.logevent':271 'anti':413 'anti-pattern':412 'antigrav':3 'api':51 'appear':359 'applic':577 'ask':621 'async':330,386 'asynchron':360 'avoid':55,416 'await':361,407 'awesom':4 'bad':425,445 'badg':227 'bash':526 'beforeeach':301 'behavior':41,45,65,419,423,435,498,521 'behavior-driven':40 'best':485 'block':510 'boundari':629 'bug':568 'busi':53 'button':327,384,406 'call':324 'case':333 'category-antigravity-awesome-skills' 'clarif':623 'clear':512,596 'click':329,385 'code':26,35 'compon':96,137,145,308 'componentnam':300 'componentname.test.tsx':544 'componentprop':150,157 'const':114,153,178,206,231,267,280,387,453,466,479 'count':162 'coverag':534,538 'creat':68,90 'criteria':632 'custom':87,92,176,182,189,483 'data':192,290,338,449,495 'debug':563 'default':75,160,310 'describ':64,299,303,319,331,509,584,600 'descript':60,503 'detail':58 'develop':18 'display':225 'doe':216,439 'dri':82 'driven':17,42 'duplic':446 'edg':332 'element':343,350,358 'email':201,217,396,459,472 'empti':337,559 'entir':250 'environ':612 'environment-specif':611 'error':294,558 'execut':579 'exist':345,353 'expect':139,187,240,346,354,363,409,429,436 'expert':617 'export':113 'factori':66,143,147,193,256,443,478,490 'fail':21,38 'fals':167,293 'file':541 'fireev':372 'fireevent.changetext':394,398 'fireevent.press':402 'first':23 'fix':570 'focus':48,519 'form':382 'function':72,89,257,491 'getmockmycomponentprop':154,180 'getmockus':207,233,481 'getmockx':69 'good':432,476 'goodby':356 'grace':339 'graphql':273 'green':31 'handl':336 'hello':141,348 'hook':274 'id':197,212,455,468 'implement':24,47,57,500 'import':102,109,123,127,149,371 'inconsist':447 'input':626 'instead':420 'integr':545 'interact':321,368 'interfac':195 'isload':166 'item':291 'jane':471 'jane@test.com':473 'jest.clearallmocks':302 'jest.fn':165,262,279,389 'jest.mock':252,258,276,286 'jest.requiremock':269,282 'john':215,438,458 'john@example.com':218 'john@test.com':460 'keep':80,517 'librari':107,132,377 'limit':588 'load':292,315,318,365,557 'logev':261 'login':405 'login-button':404 'loginform':391 'maintain':84 'match':597 'minim':25 'miss':474,634 'mock':244,246,249,254,264,272,418,428,513 'mockfetchdata':430 'mocklogev':268 'mockusegetitemsqueri':281 'mockusegetitemsquery.mockreturnvalue':289 'modul':247,251 'must':344 'mycompon':185 'name':62,199,214,457,470,482,484,505 'never':32 'npm':530,535,542 'one':520 'onpress':164,325 'onsubmit':388,392,393,410 'organ':507 'output':606 'overrid':70,77,155,168,208,221 'overview':587 'partial':71,156,209 'pass':28 'password':400 'password123':401 'pattern':2,10,67,144,245,341,369,414,552 'per':522 'permiss':627 'philosophi':14 'practic':486 'product':34 'prop':146,179,186,311,493 'properti':79 'provid':73,99 'public':50 'queri':340 'react':152,550 'react-ui-pattern':549 'react.reactelement':117 'real':422 'refactor':29 'render':88,93,103,119,136,174,304,307,314 'renderwiththem':115,124,138,184,236,390 'reproduc':567 'requir':54,98,625 'return':118,158,211 'reusabl':477 'review':618 'role':203,219,234,461,475 'run':524,527,532,536,539 'safeti':628 'scope':599 'screen':128,373 'screen.findbytext':364 'screen.getbylabeltext':395,399 'screen.getbytestid':403 'screen.getbytext':140,188,241,347,437 'screen.querybytext':355 'sensibl':74 'sickn33':8 'skill':5,6,548,575,591 'source-sickn33' 'specif':78,540,613 'src/utils/testutils.tsx':101 'state':316,556 'stop':619 'string':198,200,202 'structur':297 'submit':381 'substitut':609 'success':560,631 'systemat':562 'systematic-debug':561 'task':595 'tdd':19 'test':1,9,13,16,22,39,43,44,56,61,81,85,106,131,171,266,288,296,376,417,426,433,448,451,464,497,504,515,518,523,525,529,531,537,543,553,565,615 'test-driven':15 'testing-librari':105,130,375 'themeprovid':110 'titl':159,161,177,181,183,190 'tobenul':357 'tobetruthi':142,191,243,349,366,440 'tohavebeencal':411,431 'treat':604 'typescript':100,122,148,194,248,275,298,342,370,424,444 'ui':116,120,551,555 'undefin':295 'usag':121,169,222 'use':59,442,489,502,573,589 'usegetitemsqueri':278,284 'user':196,205,210,220,230,232,238,239,320,367,454,462,467,480 'user@example.com':397 'usercard':237 'util':12,86 'utils/analytics':253,259,270 'utils/testutils':126 'valid':614 'waitfor':362,408 'without':36 'workflow':581 'wrap':95 'write':20,33,564","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-04-25T06:52:09.217Z"},{"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-04-25T07:40:49.555Z"}],"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","source":"skills_sh","category":"antigravity-awesome-skills","skills_sh_url":"https://skills.sh/sickn33/antigravity-awesome-skills/testing-patterns"},"updatedAt":"2026-04-25T07:40:49.555Z"}}