React Native & Jest: Mock Platform OS and color scheme
When you have a React Native application and want to feature test your components. Sooner or later, you will check how the component renders from an iOS, iPad or Android device.
The tricky part is not adding support to the device. That comes by default from the framework. The complicated part is to tell the testing framework that you want to render a component in a particular device.
Here I’m going to list the solutions I found trying to add those.
🔨 What libraries am I using?
For this purpose, I’m currently using the following framework versions:
- React: 18
- React Native: 0.70.2
- Jest: 29.1
- Typescript: 4.8
@testing-library/react-native: 11.2.0 (for testing JSX components)
💻 Mocking device OS
Detecting OS as a helper function
As you might know, we have the
Platform.OS option to detect over which platform we are running our code. To make testing and/or reusing the code in our project more straightforward, I prefer to have a “Helper” file. For the next example, I’m using ESM syntax, but you can still use CommonJS module one.
Let’s add a static method so we won’t need to instantiate the class. Rather than
new Helpers().isIpad()we can just use
Now, how can we test this simple function against different devices and operating systems? Let’s see the next test that I wrote after many hours of struggling to find overrides.
I’m also adding some edge cases to try to reach 100% of the code’s coverage case.
Remember that react native would still detect an iPad device as an
Writing the tests
Let’s test that our function can run over Android.
As you can see, we can use
Platform.OS to override our platform per test.
An iPad device will be treated as they use
ios operating system. So, how do we force React Native always to retrieve
Here is where spies come to help us. Since
isIpadis a getter function, this is an easier way to override it. Then I’d prefer to use
mockReturnValueOnce here to specify when the functions execute once, it will retrieve that first time the value that I need.
For Typescript, we will use
PlatformIOSStatic . This is a way to tell TS that
Platform types (and functions) will be referring to an iOS / iPad device.
Once we learned that, we could add some tests to cover any uncommon or edge case.
Since some tests can run in parallel, we can have some test pollution. It means that some tests values could override other ones from the same group. Do you see how I need to do a
Platform.OS = 'ios' for every test? If you want a default platform, you can wrap your test in a
describe function and add an
afterEach . With this, we can override the platform and set an initial one after every test.
🎨 Mocking useColorScheme hook
Ok, now we can override the platform. But what about dark mode? Whether you are using Android or iOS, dark mode will be useful, especially at night or in dark environments. From React Native, we can conditional code some styles based on that. And we have native access to the
useColorSchemehook. But how can we mock it?
First, let’s see an example using the hook to implement the dark mode inside a component.
My ColoredStatusBar component
This function changes the color of the status bar, only validating if the OS is
ios and with light mode. That file
GlobalStyles are React Native style declarations as an object. The
useIsFocused is a React Navigation hook to detect if the current app is focused.
Current hook implementation
If we go to the source code of that
useColorScheme we will find the following code. This is React Native code at the version specified before.
They are using ESM exports to export a function by default. Mocking it would be different.
Let’s see how we can snapshot test the component.
Creating a snapshot test if device is iPad but in Dark Mode
Some notes here:
- We use
doMockto avoid test pollution. See some docs here. And we return
darkat first call, because if we don’t mock it, it will return
- We are testing that at least we are calling
useIsFocusedhook once (1st render).
- We use
toJSONto snapshot test the component.