--- name: mobile-testing description: Write and run tests for React Native apps using Jest and React Native Testing Library. Use when creating tests, debugging failures, or setting up test infrastructure. allowed-tools: Bash, Read, Write, Edit --- # Mobile Testing Testing guide for React Native applications. ## When to Use - Writing unit tests for components or utilities - Creating integration tests for features - Setting up test infrastructure - Debugging test failures - Improving test coverage ## Test Setup ```bash # Install testing dependencies npm install --save-dev jest @testing-library/react-native # Add to package.json { "scripts": { "test": "jest", "test:watch": "jest --watch", "test:coverage": "jest --coverage" } } ``` ## Component Test Template ```typescript import { render, fireEvent } from '@testing-library/react-native'; import { MyButton } from './MyButton'; describe('MyButton', () => { it('renders correctly', () => { const { getByText } = render(); expect(getByText('Click me')).toBeTruthy(); }); it('calls onPress when pressed', () => { const onPress = jest.fn(); const { getByText } = render( ); fireEvent.press(getByText('Click me')); expect(onPress).toHaveBeenCalledTimes(1); }); }); ``` ## Hook Test Template ```typescript import { renderHook, act } from '@testing-library/react-native'; import { useCounter } from './useCounter'; describe('useCounter', () => { it('increments count', () => { const { result } = renderHook(() => useCounter()); act(() => { result.current.increment(); }); expect(result.current.count).toBe(1); }); }); ``` ## Utility Test Template ```typescript import { formatDate } from './formatDate'; describe('formatDate', () => { it('formats date correctly', () => { const date = new Date('2025-01-15'); expect(formatDate(date)).toBe('2025-01-15'); }); it('handles invalid input', () => { expect(() => formatDate(null)).toThrow(); }); }); ``` ## Mocking Patterns ### Mock External Module ```typescript jest.mock('expo-notifications', () => ({ scheduleNotificationAsync: jest.fn(), })); ``` ### Mock Database ```typescript jest.mock('@/db/client', () => ({ db: { select: jest.fn(), insert: jest.fn(), }, })); ``` ### Mock Navigation ```typescript jest.mock('expo-router', () => ({ useRouter: () => ({ push: jest.fn(), back: jest.fn(), }), })); ``` ## Running Tests ```bash # Run all tests npm test # Watch mode (auto-rerun on changes) npm run test:watch # With coverage report npm run test:coverage # Run specific test file npm test -- MyComponent.test.tsx # Update snapshots npm test -- -u ``` ## Best Practices 1. **Test Behavior, Not Implementation**: Test what users see and do 2. **Use `testID` for Selection**: More reliable than text matching 3. **Mock External Dependencies**: Keep tests isolated and fast 4. **Test Edge Cases**: Empty states, errors, loading states 5. **Keep Tests Simple**: One assertion per test when possible 6. **Clean Up**: Use `beforeEach`/`afterEach` for setup/teardown ## Common Patterns ### Async Testing ```typescript it('loads data', async () => { const { findByText } = render(); expect(await findByText('Loaded')).toBeTruthy(); }); ``` ### Testing Forms ```typescript it('validates input', () => { const { getByTestId, getByText } = render(); fireEvent.changeText(getByTestId('email-input'), 'invalid'); fireEvent.press(getByTestId('submit-button')); expect(getByText('Invalid email')).toBeTruthy(); }); ``` ### Testing Lists ```typescript it('renders list items', () => { const items = [{ id: '1', name: 'Item 1' }]; const { getAllByTestId } = render(); expect(getAllByTestId('list-item')).toHaveLength(1); }); ``` ## Troubleshooting - **Tests timing out**: Increase timeout or check for unresolved promises - **Can't find element**: Use `screen.debug()` to see rendered output - **Mock not working**: Ensure mock is before import - **Async issues**: Use `waitFor` or `findBy` queries ## Resources - [Jest Docs](https://jestjs.io/) - [React Native Testing Library](https://callstack.github.io/react-native-testing-library/)