In React Native Jest tests, what is Platform.OS by default, and how do you test a component's Android branch?
answer
- haste defaultPlatform in the preset
- Platform.ios.js is the one loaded
- select() is hard-wired per platform file
- import-time constants do not change
- a second Jest project for android
basics
~20 sPlatform.OS is 'ios' in tests because the React Native preset's haste defaultPlatform is 'ios', so Platform.ios.js and .ios files load. Test Android branches with a second Jest project whose defaultPlatform is 'android', or by injecting the value.
solid answer
~30 s`@react-native/jest-preset` sets `haste.defaultPlatform` to `'ios'`, so Jest resolves `Platform.ios.js`, whose `OS` is `'ios'`, and prefers `Foo.ios.tsx` over `Foo.android.tsx`. Assigning `Platform.OS = 'android'` inside a test only changes code that reads `Platform.OS` at call time: `Platform.select` from `Platform.ios.js` still returns the `ios` key, platform files are already resolved, and constants computed at import keep their iOS value. The reliable way to test Android is a second Jest project with `haste.defaultPlatform: 'android'`, which loads `Platform.android.js` and `.android` files for the whole run. For one branch in one component, inject the platform value through a prop or a small module you can mock.
code
javascript · 14 lines// jest.config.js
const base = { preset: '@react-native/jest-preset' };
module.exports = {
projects: [
{ ...base, displayName: 'ios' },
{
...base,
displayName: 'android',
haste: { defaultPlatform: 'android', platforms: ['android', 'ios', 'native'] },
testMatch: ['<rootDir>/src/platform/**/*.test.tsx'],
},
],
};go deeper
Recall that Platform.OS is 'ios' in React Native's default Jest setup and that Android branches need deliberate extra setup.
Explain the preset's haste.defaultPlatform, why assigning Platform.OS misses select, platform files and import-time constants, and how to restore it.
Set up an Android Jest project or platform injection so both branches run reliably, and scope it to code that actually branches.
Decide how much per-platform logic belongs in shared code at all, since every branch doubles the tests needed to trust it.
## Where Platform.OS comes from in Jest In an app, `Platform` is resolved per platform by Metro: the Android bundle includes `Platform.android.js` and the iOS bundle includes `Platform.ios.js`. Each file exports an object whose `OS` field is a fixed string. Jest has no bundle target, so the React Native preset tells Jest's resolver which platform to prefer: ```js haste: { defaultPlatform: 'ios', platforms: ['android', 'ios', 'native'], }, ``` With `defaultPlatform: 'ios'`, every import of a module that has platform variants resolves the `.ios` file first. That includes React Native's own `Platform.ios.js` (so `Platform.OS === 'ios'`) and your app's own files such as `DatePickerField.ios.tsx`. ## Why assigning Platform.OS is only a partial fix `Platform` is a plain object, so a test can write `Platform.OS = 'android'`. That changes **some** behaviour and leaves the rest on iOS: | What the code does | Does `Platform.OS = 'android'` switch it? | Why | |---|---|---| | Reads `Platform.OS` inside a function at render time | Yes | The value is read after the assignment | | Calls `Platform.select({ ios, android })` | No | `Platform.ios.js` implements `select` by checking for the `ios` key, not by reading `OS` | | Imports `./Picker` with `Picker.ios.tsx` and `Picker.android.tsx` | No | The file was resolved to the `.ios` variant when the module loaded | | Computes a constant at module top level from `Platform.OS` | No | It ran at import, before the test's assignment | | Reads `Platform.Version` or other constants | No | They come from the iOS constants module, which the preset mocks | There is also a hygiene cost: the assignment mutates a shared object, so it must be restored afterwards or it leaks into later tests in the same file. ## Reliable ways to test the Android branch 1. **A second Jest project.** Jest's `projects` option runs the same tests under several configurations. A project that spreads the React Native preset and sets `haste.defaultPlatform: 'android'` loads `Platform.android.js` and every `.android` file, so `Platform.OS`, `Platform.select` and platform files all agree. The cost is running the chosen tests twice; many teams limit the Android project to the folders that branch on platform. 2. **Isolate modules per test.** Inside `jest.isolateModules`, change the platform and re-require the component so import-time code runs again. This works for `Platform.OS` reads but still cannot change how files resolve, so it is weaker than a project. 3. **Inject the platform.** Pass `platform` as a prop, or read it through a tiny module of your own (`getPlatform()`) that tests mock. This makes branch coverage explicit and keeps tests independent of React Native's resolution. ## What to test per platform, and what not to - Test **your** logic that differs per platform: which label, which component, which handler. - Do not try to test native platform behaviour in Jest, such as the Android back button closing a `Modal` or iOS keyboard avoidance; the preset mocks those components, so the test would only check the mock. - Keep the default iOS run as the main suite and add the Android project where branches exist, rather than doubling everything. ## Common mistakes in platform tests - **Asserting on the mock instead of the logic.** A snapshot that differs between the iOS and Android projects only because a mocked core component renders differently tells you nothing about your code. - **Mutating `Platform` in one test without restoring it.** Later tests in the file silently run the Android branch. - **Branching on `Platform.OS` deep inside many components.** Each branch needs its own test; pushing the decision into one module, or into platform files, keeps the number of branches small and testable. - **Expecting `Platform.Version` to match a real OS.** Under the preset it comes from mocked constants, so code that branches on OS version needs the value injected or mocked explicitly. ## Interview framing A junior answer says "`ios`, and I set `Platform.OS`". A stronger answer explains that the default comes from the preset's `haste` block, lists what the assignment does not change (`select`, resolved files, import-time constants) and proposes a second Jest project for real Android coverage.
- A component renders DatePickerField, which has .ios.tsx and .android.tsx variants; which one does a default React Native Jest run render?The `.ios.tsx` variant. The preset's `haste.defaultPlatform` is `'ios'`, so Jest resolves the iOS file when the module loads, and assigning `Platform.OS` in the test afterwards cannot swap the already-resolved import. An Android Jest project, or importing the Android file directly in a dedicated test, covers the other variant.
- Why must a test that assigns Platform.OS restore it?`Platform` is a shared module object within the test file. An assignment in one test stays in effect for every later test in that file, so a forgotten reset makes later tests run the Android branch unexpectedly. Save the original in `beforeEach` or restore it in `afterEach`.
saying these in an interview costs you the question
- Platform.OS is undefined in Jest because there is no device
- Setting Platform.OS = 'android' makes Platform.select return the android key
- Jest loads Android and iOS platform files at random
- Platform-specific .android.tsx files are picked up after assigning Platform.OS
- The React Native preset defaults tests to the Android platform