Mocking platform detection in Jest and React Nativ

2020-02-26 04:17发布

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.

13条回答
趁早两清
2楼-- · 2020-02-26 04:30

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
}));
查看更多
成全新的幸福
3楼-- · 2020-02-26 04:38

You can mock whatever you want from React-Native like this:

describe('notifications actions tests', () => {
  let Platform;


  beforeEach(() => {
    jest.mock('react-native', () => ({
          Platform: {
           ... 
        }));


    Platform = require('react-native').Platform; // incase u would like to refer to Platform in your tests
  });
查看更多
贼婆χ
4楼-- · 2020-02-26 04:39

use jest.doMock and jest.resetModules

jest.resetModules()
jest.doMock('react-native', () => ({ Platform: { OS: 'android' }}))
查看更多
对你真心纯属浪费
5楼-- · 2020-02-26 04:43

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.

查看更多
Explosion°爆炸
6楼-- · 2020-02-26 04:44

Since the other answers will not work if you want to mock different OSs in the same test suite and in one test run, here's another way. Instead of using Platform.OS directly in your code, define a helper function somewhere and use that to get references to the OS in your components:

in 'helpers.js':

export function getOS() {
  return Platform.OS;
}

in your component:

import * as helpers from './helpers';
render() {
    if (helpers.getOS() === 'android') {// do something}
}

This function can then be mocked it in your tests, e.g.

import * as helpers from './helpers';

// ...

it('does something on Android', () => {
  jest.spyOn(helpers, 'getOS').mockImplementation(() => 'android');
  // ...
}

it('does something else on iOS', () => {
  jest.spyOn(helpers, 'getOS').mockImplementation(() => 'ios');
  // ...
}

Credit for the idea goes to this GitHub issue comment.

查看更多
【Aperson】
7楼-- · 2020-02-26 04:44
import React from "react";
import renderer from "react-test-renderer";
import SmartText from "../SmartText";

describe("markdown smart text component", () => {
  beforeEach(() => {
    jest.resetModules();
  });

  it("renders with props on ios", () => {
    jest.mock("Platform", () => {
      return { OS: "ios" };
    });
    expect(
      renderer.create(<SmartText title="code ios" code />).toJSON()
    ).toMatchSnapshot();
  });

  it("renders with props on android", () => {
    jest.mock("Platform", () => {
      return { OS: "android" };
    });
    expect(
      renderer.create(<SmartText title="code android" code />).toJSON()
    ).toMatchSnapshot();
  });
});
查看更多
登录 后发表回答