Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking platform detection in Jest and React Native

Some of the code I am trying to test detects the platform, using, e.g.:

import { Platform } from 'react-native';
...

if (Platform.OS === 'android') {
  ...
} else {
  ...
}

Is there a sensible way to mock this with Jest and/or something else, so I can test both branches in one test run?

Or is the smart way to decouple it and put the platform into, e.g., a context variable? Although it always feels restructuring code to make it easier to test is something of a cheat.

like image 957
Stuart Watt Avatar asked Apr 01 '17 20:04

Stuart Watt


4 Answers

This worked for me (Jest 21.2.1, Enzyme 3.2.0):

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

Put it either at the top of your test, or in a beforeAll for example.

like image 192
Bramus Avatar answered Sep 23 '22 10:09

Bramus


For everyone looking for this, what it helped me was the following:

jest.mock('react-native/Libraries/Utilities/Platform', () => ({     OS: 'android', // or 'ios'     select: () => null })); 
like image 45
Luciano Corniglione Avatar answered Sep 25 '22 10:09

Luciano Corniglione


I implemented a small mock that allows you to change Platform during tests in the same test file.

Add this to your jest setup file

jest.mock('react-native/Libraries/Utilities/Platform', () => {
  let platform = {
    OS: 'ios',
  }

  const select = jest.fn().mockImplementation((obj) => {
    const value = obj[platform.OS]
    return !value ? obj.default : value
  })

  platform.select = select

  return platform
});

Then you can easily change Platform in your test. If you are using Platform.select it will also work as expected!

import { Platform } from 'react-native'

describe('When Android', () => {
  it('should ...', () => {
    Platform.OS = 'android'
    ...
  })
})

describe('When iOS', () => {
  it('should ...', () => {
    Platform.OS = 'ios'
    ...
  })
})
like image 31
オスカー Avatar answered Sep 26 '22 10:09

オスカー


The way that I achieved mocking setting the platform was just set it directly in the tests:

it('should only run for Android', () => {
  Platform.OS = 'android'; // or 'ios'

  // For my use case this module was failing on iOS
  NativeModules.MyAndroidOnlyModule = {
    fetch: jest.fn(
      (url, event) => Promise.resolve(JSON.stringify(event.body))
    ),
  }; 
  return myParentFunction().then(() => {
    expect(NativeModules.MyAndroidOnlyModule.fetch.mock.calls.length).toBe(1);
    expect(fetch.mock.calls.length).toBe(0);
  });
});

This would setup the platform to only run on Android during tests to make sure that my function was calling only specific functions. My function that was wrapped in platform dependent compilation looked like:

export default function myParentFunction() {
  if (Platform.OS === 'ios') {
    return fetch();
  }
  return NativeModules.MyAndroidOnlyModule.fetch();
}

I would suggest just creating two different tests one with the platform set to iOS and the other to Android since ideally a function should only have one responsibility. However, I'm sure you can use this to run the first test, dynamically set the platform and run test number two all in one function.

like image 28
Boomer Rogers Avatar answered Sep 22 '22 10:09

Boomer Rogers