| name | mobile-testing |
|---|---|
| description | Set up unit and integration testing for React Native/Expo or Flutter apps. Covers Jest, React Native Testing Library, flutter_test, snapshot testing, mocking native modules, and test organization. Use when the user wants to add or improve tests for components, hooks, or business logic. |
| standards-version | 1.6.3 |
Use this skill when the user:
- Wants to add unit or integration tests to their app
- Asks about Jest, React Native Testing Library, or flutter_test
- Needs to test components, hooks, utilities, or state management
- Mentions "testing", "unit test", "snapshot test", "test coverage", or "mock"
- Wants help structuring test files or writing assertions
- Framework: Expo (React Native) or Flutter
- Test scope: components, hooks, utilities, or all
- Existing test setup: none, partial, or already configured
-
Install test dependencies (Expo). Expo projects need Jest and React Native Testing Library:
npx expo install jest jest-expo @testing-library/react-native @testing-library/jest-native
Add the Jest config to
package.json:{ "jest": { "preset": "jest-expo", "setupFilesAfterSetup": ["@testing-library/jest-native/extend-expect"], "transformIgnorePatterns": [ "node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@sentry/react-native|native-base|react-native-svg)" ] } } -
Install test dependencies (Flutter). Flutter ships with
flutter_test. For additional matchers:dev_dependencies: flutter_test: sdk: flutter mocktail: ^1.0.4
-
Organize test files. Follow the convention for each framework:
Framework Convention Example Expo __tests__/ComponentName.test.tsxnext to sourcecomponents/__tests__/Avatar.test.tsxExpo (colocated) ComponentName.test.tsxin the same directorycomponents/Avatar.test.tsxFlutter Mirror lib/structure undertest/test/widgets/avatar_test.dart -
Write a component test (Expo). Test rendering and interaction:
import { render, screen, fireEvent } from "@testing-library/react-native"; import { Counter } from "../Counter"; describe("Counter", () => { it("renders the initial count", () => { render(<Counter initialCount={5} />); expect(screen.getByText("5")).toBeTruthy(); }); it("increments on press", () => { render(<Counter initialCount={0} />); fireEvent.press(screen.getByRole("button", { name: "Increment" })); expect(screen.getByText("1")).toBeTruthy(); }); });
-
Write a widget test (Flutter).
import 'package:flutter_test/flutter_test.dart'; import 'package:my_app/widgets/counter.dart'; void main() { testWidgets('Counter increments on tap', (tester) async { await tester.pumpWidget(const MaterialApp(home: Counter(initialCount: 0))); expect(find.text('0'), findsOneWidget); await tester.tap(find.byIcon(Icons.add)); await tester.pump(); expect(find.text('1'), findsOneWidget); }); }
-
Mock native modules. Many Expo modules need mocks in
jest.setup.js:jest.mock("expo-camera", () => ({ Camera: "Camera", useCameraPermissions: () => [{ granted: true }, jest.fn()], })); jest.mock("@react-native-async-storage/async-storage", () => require("@react-native-async-storage/async-storage/jest/async-storage-mock") );
Reference the setup file in your Jest config:
{ "jest": { "setupFiles": ["./jest.setup.js"] } } -
Test custom hooks. Use
renderHookfrom React Native Testing Library:import { renderHook, act } from "@testing-library/react-native"; import { useCounter } from "../useCounter"; it("increments the counter", () => { const { result } = renderHook(() => useCounter(0)); act(() => result.current.increment()); expect(result.current.count).toBe(1); });
-
Snapshot testing. Capture component output for regression detection:
import { render } from "@testing-library/react-native"; import { ProfileCard } from "../ProfileCard"; it("matches snapshot", () => { const tree = render(<ProfileCard name="Alice" />); expect(tree.toJSON()).toMatchSnapshot(); });
Update snapshots when intentional changes are made:
npx jest --updateSnapshot. -
Run tests. Execute the suite and check results:
# Expo npx jest npx jest --coverage npx jest --watch # Flutter flutter test flutter test --coverage
- Jest: Getting Started
- React Native Testing Library
- Expo: Testing
- Flutter: Testing
- Mocktail for Flutter
User: "I want to add tests to my Expo app. I have components and custom hooks but no tests yet."
Agent:
- Installs jest-expo, @testing-library/react-native, and @testing-library/jest-native with
mobile_installDependency - Adds Jest config to package.json with the jest-expo preset and transform ignore patterns
- Creates
jest.setup.jswith mocks for native modules used in the project - Generates a test file for a key component using
mobile_generateTestFile - Writes a
renderHooktest for the most-used custom hook - Runs
mobile_runTeststo verify everything passes - Suggests adding
--coverageto track test coverage over time
| Step | MCP Tool | Description |
|---|---|---|
| Install test libs | mobile_installDependency |
Install jest-expo, @testing-library/react-native |
| Generate test file | mobile_generateTestFile |
Scaffold test boilerplate for an existing component |
| Run test suite | mobile_runTests |
Execute tests and report pass/fail summary |
| Verify build | mobile_checkBuildHealth |
Ensure test setup does not break the build |
- Missing transform ignore patterns - Jest fails on untranspiled node_modules. The
transformIgnorePatternsarray must include every RN-related package that ships untranspiled ESM or JSX. - Testing implementation details - Test behavior (what the user sees), not internal state. Query by text, role, or testID, not by component internals.
- Stale snapshots - Snapshots become noise if updated blindly. Review every snapshot diff before committing. Delete snapshots that test layout rather than logic.
- Not mocking native modules - Tests crash if native modules like expo-camera or expo-notifications are not mocked. Add mocks in
jest.setup.js. - Async state not awaited - Use
waitFororfindBy*queries for components that update state asynchronously. BaregetBy*queries miss async renders. - Flutter test imports - Always import from
package:your_app/...in tests, not relative paths. The test runner resolves packages differently.
- Mobile E2E Testing - end-to-end testing with Detox, Maestro, and Patrol
- Mobile CI/CD - run tests automatically in GitHub Actions
- Mobile Component Patterns - testable component architecture