Jest: Platform.select defaults to iOS when explicitly setting Platform with jest.mock

Created on 9 Sep 2017  ·  9Comments  ·  Source: facebook/jest

Below I am testing a component to have a red background in iOS and a blue background in Android. However, the jest snapshot always defaults to iOS if your component uses react native's method: Platform.select.

component.js

import React from 'react';
import {Text, View, Platform, StyleSheet} from 'react-native'

const TestComponent = () => <View style={styles.container}><Text>Hello Jest</Text></View>

const styles = StyleSheet.create({
  container: {
    flex: 1,
    ...Platform.select({
      ios: {
        backgroundColor: 'red',
      },
      android: {
        backgroundColor: 'blue',
      },
    }),
  },
});
export default TestComponent; 

component.spec.js

import 'react-native';
import React from 'react';
import renderer from 'react-test-renderer';
import TestComponent from '.add.js';

jest.mock('Platform', () => {
  const Platform = require.requireActual('Platform');
  Platform.OS = 'android';
  return Platform;
});

describe('TestComponent', () => {
  it('renders default android ui', () => {
    const tree = renderer
      .create(<TestComponent />)
      .toJSON();
    expect(tree).toMatchSnapshot();
  });
});

component.js.snap

// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`TestComponent renders default android ui 1`] = 
<View
  style={
    Object {
      "backgroundColor": "red",
      "flex": 1,
    }
  }
>
  <Text
    accessible={true}
    allowFontScaling={true}
    disabled={false}
    ellipsizeMode="tail"
  >Hello Jest </Text>
</View>;

If I change component.js to the following (removing Platform.select with an alternative option):

import React from 'react';
import { Text, Platform, StyleSheet, View } from 'react-native';

const TestComponent = () =>
  <View style={styles.container}>
    <Text>Hello Jest</Text>
  </View>;

const color = Platform.OS === 'ios' ? 'red' : 'blue';
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: color,
  },
});
export default TestComponent;

npm run test displays the following error showing jest's snapshot is now displaying the desirable blue background for android.

TestComponent › renders default android ui

    expect(value).toMatchSnapshot()

    Received value does not match stored snapshot 1.

    - Snapshot
    + Received

    @@ -1,9 +1,9 @@
      <View
        style={
          Object {
    -       "backgroundColor": "red",
    +       "backgroundColor": "blue",
            "flex": 1,
          }
        }
      >
        <Text

      at Object.<anonymous> (__tests__/components/test.js:15:14)
      at tryCallTwo (node_modules/promise/lib/core.js:45:5)
      at doResolve (node_modules/promise/lib/core.js:200:13)
      at new Promise (node_modules/promise/lib/core.js:66:3)
      at tryCallOne (node_modules/promise/lib/core.js:37:12)
      at node_modules/promise/lib/core.js:123:15

 › 1 snapshot test failed.
  TestComponent
    ✕ renders default android ui (34ms)

Most helpful comment

Having the same issue, I have an utility function like:

const mockPlatform = (OS, version) => {
  jest.resetModules();
  jest.doMock("Platform", () => ({
    OS,
    select: objs => objs[OS],
    Version: version || undefined
  }));
};

then I do

mockPlatform("android", 23);

inside the test but when I console.log Platform I get:

{ OS: 'ios',
        Version: [Getter],
        isPad: [Getter],
        isTVOS: [Getter],
        isTV: [Getter],
        isTesting: [Getter],
        select: [Function: select] }

and then

{ OS: 'android', select: [Function: select], Version: 23 }

after

All 9 comments

Unfortunately mocking Platform isn't the only thing that requires changing. The default is probably iOS from the preset, but you can provide your own config for the "haste" field that overwrites the default platform: See https://github.com/facebook/react-native/blob/master/jest-preset.json#L3

Try:

  "haste": {
    "defaultPlatform": "android",
    "platforms": ["android", "ios", "native"],
    "providesModuleNodeModules": [
      "react-native"
    ]
  }

In your Android config.

Please note that there is currently no good way to toggle between two configs. You could however use the multi project runner :)

Based on @cpojer 's suggestion, I'm using this solution, works great.

Having the same issue, I have an utility function like:

const mockPlatform = (OS, version) => {
  jest.resetModules();
  jest.doMock("Platform", () => ({
    OS,
    select: objs => objs[OS],
    Version: version || undefined
  }));
};

then I do

mockPlatform("android", 23);

inside the test but when I console.log Platform I get:

{ OS: 'ios',
        Version: [Getter],
        isPad: [Getter],
        isTVOS: [Getter],
        isTV: [Getter],
        isTesting: [Getter],
        select: [Function: select] }

and then

{ OS: 'android', select: [Function: select], Version: 23 }

after

Fixed by doing

  beforeEach(() => {
    jest.restoreAllMocks();
    mockPlatform("android", 24);
  });

Just set Platform.OS in your tests, I did not need a mock, and I like to stay away from mocking frameworks personally.

Bumped on the same problem. It seems like haste for now is the only way to solve this.
Created a dummy test project to have a possibility to run 2 set of tests (iOS and Android):

https://github.com/vyarmak/react-native-platform-testing

haste still works but it prints warning on the console

● Validation Warning:

  Unknown option "haste.providesModuleNodeModules" with value ["react-native"] was found.
  This is probably a typing mistake. Fixing it will remove this message.

  Configuration Documentation:
  https://jestjs.io/docs/configuration.html

● Validation Warning:

  Unknown option "haste.providesModuleNodeModules" with value ["react-native"] was found.
  This is probably a typing mistake. Fixing it will remove this message.

  Configuration Documentation:
  https://jestjs.io/docs/configuration.html

Unfortunately mocking Platform isn't the only thing that requires changing. The default is probably iOS from the preset, but you can provide your own config for the "haste" field that overwrites the default platform: See https://github.com/facebook/react-native/blob/master/jest-preset.json#L3

Try:

  "haste": {
    "defaultPlatform": "android",
    "platforms": ["android", "ios", "native"],
    "providesModuleNodeModules": [
      "react-native"
    ]
  }

In your Android config.

Please note that there is currently no good way to toggle between two configs. You could however use the multi project runner :)

hey @cpojer anything new for platform setting?

Alright I get going on this:
found this: https://github.com/facebook/react-native/blob/master/jest-preset.js

and removed providesModuleNodeModules. My tests are shined again ✨

Was this page helpful?
0 / 5 - 0 ratings