Setting Up Jest in a React Native Project
Configure Jest with the react-native preset, run your first test file, understand the test, describe, and expect API, and set up code coverage reporting.
Setting Up Jest in a React Native Project is a free React Native Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the React Native Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Why Testing React Native Code
Tests are your safety net when making changes. A well-written test suite lets you refactor confidently, catch regressions before they reach users, and document expected behavior for your teammates. For React Native specifically, tests are especially valuable because running the full app on a simulator is slow compared to running unit tests.
There are three levels of testing in React Native: unit tests (individual functions and hooks), component tests (rendering components and asserting on output), and end-to-end tests (driving the real app on a device). Jest handles the first two.
Jest Comes Pre-Configured
React Native projects created with the React Native CLI or Expo SDK already include Jest. You will find jest in devDependencies and a Jest configuration in package.json under the 'jest' key, or in a jest.config.js file.
The preset 'react-native' (from @react-native/jest-preset) or 'jest-expo' (for Expo) configures Jest to transform JSX and handle native module mocks automatically. Always use the appropriate preset for your project type.
// package.json (React Native CLI project)
{
'jest': {
'preset': 'react-native'
}
}
// package.json (Expo project)
{
'jest': {
'preset': 'jest-expo',
'transformIgnorePatterns': [
'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*)'
]
}
}Running Your First Test
Run tests with npx jest or just yarn test. Jest finds all files matching *.test.ts, *.test.tsx, *.spec.ts, or files inside __tests__ folders. Add --watch to rerun tests automatically when files change — this is the recommended workflow during development.
A newly scaffolded project includes a sample test in App.test.tsx. Running it confirms your Jest setup is working before you write your own tests.
# Run all tests once
npx jest
# Run in watch mode (rerun on file change)
npx jest --watch
# Run a specific test file
npx jest src/utils/formatDate.test.ts
# Run tests matching a pattern
npx jest --testNamePattern='should format'The Anatomy of a Test
A Jest test file uses three core functions: describe groups related tests, it (or test) defines a single test case, and expect makes assertions about values. The pattern is: arrange (set up data), act (run the code), assert (verify the output).
Test names should describe the expected behavior in plain English. Good test names read like documentation: 'should return null when the input is empty' is clearer than 'test1'.
// src/utils/formatDate.test.ts
import { formatDate } from './formatDate';
describe('formatDate', () => {
it('should format a date as MMM DD, YYYY', () => {
// Arrange
const date = new Date('2024-03-15T10:00:00Z');
// Act
const result = formatDate(date);
// Assert
expect(result).toBe('Mar 15, 2024');
});
it('should return null when the input is null', () => {
expect(formatDate(null)).toBeNull();
});
});Common Jest Matchers
Jest provides a rich set of matchers — methods on the expect object that check values in different ways. The most common matchers for React Native tests are:
toBe(value)— strict equality with===toEqual(value)— deep equality for objects and arraystoBeTruthy()/toBeFalsy()— truthy/falsy checkstoContain(item)— array contains itemtoThrow()— function throws an error
// Strict equality (for primitives)
expect(2 + 2).toBe(4);
// Deep equality (for objects)
expect({ name: 'Alice', age: 30 }).toEqual({ name: 'Alice', age: 30 });
// Array checks
expect(['a', 'b', 'c']).toContain('b');
expect(['a', 'b', 'c']).toHaveLength(3);
// Null / undefined
expect(null).toBeNull();
expect(undefined).toBeUndefined();
// Error throwing
expect(() => JSON.parse('invalid')).toThrow(SyntaxError);Testing Pure Functions
The easiest things to test are pure functions — functions that take input, return output, and have no side effects. Examples include validation functions, data transformers, and utility helpers. These tests are fast and require no mocking.
Write one test per edge case. Test the happy path (valid input), boundary conditions (empty string, zero, null), and error cases (invalid input). This is where your test-to-code ratio should be highest.
// src/utils/validators.ts
export function isValidEmail(email: string): boolean {
return /^[^@]+@[^@]+\.[^@]+$/.test(email);
}
// src/utils/validators.test.ts
import { isValidEmail } from './validators';
describe('isValidEmail', () => {
it('should return true for a valid email', () => {
expect(isValidEmail('user@example.com')).toBe(true);
});
it('should return false for an email without @', () => {
expect(isValidEmail('notanemail.com')).toBe(false);
});
it('should return false for an empty string', () => {
expect(isValidEmail('')).toBe(false);
});
});Mocking with jest.fn()
jest.fn() creates a mock function — a fake implementation that records how it was called. You pass mock functions as props to components or inject them as dependencies to isolate the code under test from its real collaborators.
After running the code under test, assert on the mock using matchers like toHaveBeenCalled(), toHaveBeenCalledWith(args), and toHaveBeenCalledTimes(n). This lets you verify that your code correctly calls external APIs or callbacks without actually making network requests.
// Test that a submit handler calls the API with the correct arguments
const mockSubmit = jest.fn().mockResolvedValue({ success: true });
await handleFormSubmit({ email: 'user@test.com' }, mockSubmit);
expect(mockSubmit).toHaveBeenCalledTimes(1);
expect(mockSubmit).toHaveBeenCalledWith({ email: 'user@test.com' });
// Mock return values
const mockFetch = jest.fn().mockResolvedValue([{ id: '1', title: 'Post' }]);
const posts = await fetchPosts(mockFetch);
expect(posts).toHaveLength(1);Module Mocking with jest.mock()
jest.mock('module-name') replaces an entire module with an auto-mocked version where all exported functions become jest.fn(). This is essential for mocking native modules, which cannot run in the Jest Node.js environment.
Call jest.mock() at the top of the test file. Jest hoists mock calls to the top of the file automatically, so they are set up before any imports run. Use jest.requireActual to preserve parts of the real module.
// Mock expo-location
jest.mock('expo-location', () => ({
requestForegroundPermissionsAsync: jest.fn().mockResolvedValue({ status: 'granted' }),
getCurrentPositionAsync: jest.fn().mockResolvedValue({
coords: { latitude: 37.78825, longitude: -122.4324 },
}),
}));
// Mock React Navigation
jest.mock('@react-navigation/native', () => ({
...jest.requireActual('@react-navigation/native'),
useNavigation: () => ({ navigate: jest.fn() }),
useRoute: () => ({ params: { userId: '123' } }),
}));Code Coverage Reports
Jest can measure code coverage — what percentage of your code is executed by tests. Run jest --coverage to generate a coverage report in the terminal and an HTML report in coverage/lcov-report/index.html.
The report shows four metrics per file: Statements (lines of code executed), Branches (if/else paths taken), Functions (functions called), and Lines. Focus on covering critical business logic, error handling paths, and utility functions — 80-90% coverage on those is more valuable than 100% coverage on simple render components.
# Run tests with coverage report
npx jest --coverage
# Coverage thresholds in jest.config.js
module.exports = {
coverageThreshold: {
global: {
statements: 80,
branches: 70,
functions: 80,
lines: 80,
},
},
};Setup and Teardown with beforeEach and afterEach
beforeEach and afterEach run setup and cleanup code around each test in a describe block. Use beforeEach to reset mocks, initialize test data, or create a fresh component instance. Use afterEach to clean up timers, subscriptions, or async operations.
beforeAll and afterAll run once before/after all tests in a describe block — useful for expensive setup like opening a database connection that is shared across tests.
describe('AuthService', () => {
let mockStorage: Record<string, string>;
beforeEach(() => {
// Fresh state before each test
mockStorage = {};
jest.clearAllMocks(); // Reset mock call counts
});
afterEach(() => {
// Clean up any timers started during the test
jest.useRealTimers();
});
it('should save the token to storage on sign in', async () => {
await signIn('user@test.com', 'password', mockStorage);
expect(mockStorage['authToken']).toBeDefined();
});
});Testing Async Code with async/await
Most React Native operations are asynchronous. Jest supports async/await directly in test functions. Return the promise (or use async/await) so Jest knows to wait for it to resolve before marking the test as passed.
For code that uses setTimeout or setInterval, use jest.useFakeTimers() to control time in tests. Call jest.runAllTimers() to fast-forward all pending timers without actually waiting.
// Testing async functions
it('should return sorted posts from the API', async () => {
const posts = await fetchAndSortPosts();
expect(posts[0].createdAt).toBeGreaterThan(posts[1].createdAt);
});
// Testing code with timers
it('should call the callback after 1 second', () => {
jest.useFakeTimers();
const callback = jest.fn();
scheduleCallback(callback, 1000);
expect(callback).not.toHaveBeenCalled();
jest.advanceTimersByTime(1000);
expect(callback).toHaveBeenCalledTimes(1);
});Quick Check
Test your understanding of React Native Mobile Development concepts from this lesson.
Lesson Recap
In this lesson you learned: how Jest is configured in React Native and Expo projects via presets, how to write unit tests for pure functions using the arrange-act-assert pattern, and how to mock functions and modules with jest.fn() and jest.mock() to isolate code under test. Next up we render React Native components in tests using React Native Testing Library.
Frequently asked questions
Is the “Setting Up Jest in a React Native Project” lesson free?
Yes — the full text of “Setting Up Jest in a React Native Project” is free to read here on the web, and the React Native Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the React Native Academy course, upgrade to CoddyKit PRO.
What will I learn in “Setting Up Jest in a React Native Project”?
Configure Jest with the react-native preset, run your first test file, understand the test, describe, and expect API, and set up code coverage reporting. You practise React Native Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start React Native Academy?
No prior experience is required. React Native Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Setting Up Jest in a React Native Project” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this React Native Academy lesson?
Yes. Every React Native Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Setting Up Jest in a React Native Project
- Rendering Components with React Native Testing Library
- Firing Events and Testing User Interaction
- Mocking Native Modules and Async Code