问题 在Jest和React Native中模拟平台检测


我试图测试的一些代码检测平台,例如使用:

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

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

是否有一种明智的方法可以用Jest和/或其他东西来模拟这个,所以我可以在一次测试中测试两个分支?

或者是将它解耦并将平台放入例如上下文变量的智能方法?虽然总是感觉重组代码使其更容易测试是一种欺骗。


9291
2018-04-01 20:02


起源



答案:


这对我有用 (Jest 21.2.1,Enzyme 3.2.0)

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

把它放在测试的顶部,或者放在 一个 beforeAll 例如。


11
2017-12-01 15:23



对我不起作用。有什么建议么? - Andrei Konstantinov
任何有流量问题的人:使用 jest.requireActual 代替 require.requireActual - David Nathan
它对我不起作用 - marco


答案:


这对我有用 (Jest 21.2.1,Enzyme 3.2.0)

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

把它放在测试的顶部,或者放在 一个 beforeAll 例如。


11
2017-12-01 15:23



对我不起作用。有什么建议么? - Andrei Konstantinov
任何有流量问题的人:使用 jest.requireActual 代替 require.requireActual - David Nathan
它对我不起作用 - marco


我实现模拟设置平台的方式只是直接在测试中设置:

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);
  });
});

这将设置平台仅在测试期间在Android上运行,以确保我的功能仅调用特定功能。我在平台相关编译中包含的函数如下所示:

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

我建议只创建两个不同的测试,一个平台设置为iOS,另一个设置为Android,因为理想情况下,一个功能应该只有一个责任。但是,我确信您可以使用它来运行第一个测试,动态设置平台并在一个函数中运行第二个测试。


4
2017-07-24 21:31





如果您想要模拟不同的操作系统,其他答案将无效 在同一个测试套件中 和 在一次试运行中,这是另一种方式。而不是使用 Platform.OS 直接在你的代码中,在某处定义一个辅助函数并使用它:

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

这个功能 能够 然后在你的测试中嘲笑它,例如

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

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

这个想法值得信赖 这个GitHub问题评论


1
2018-02-09 14:04





也许是“导入”方法中的问题,请检查:

const isAndroid = require('app/helpers/is_android');

//import isAndroid from 'app/helpers/is_android'

使用“导入”这将无法正常工作,需要使用“require”。

beforeEach(() => {
  jest.resetModules();
});

it("should be true when Android", () => {
  jest.mock('Platform', () => {
    return { OS: 'android' };
  });

  expect(isAndroid).toBe(true);
});   

1
2017-07-24 08:14





使用 jest.doMock 和 jest.resetModules

jest.resetModules()
jest.doMock('react-native', () => ({ Platform: { OS: 'android' }}))

0
2017-07-26 16:27





你可以模仿你想要的任何东西 React-Native 喜欢这个:

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
  });

0
2018-04-01 20:21



这不是永久性的吗?也就是说,这并不意味着我需要“取消” afterEach?那可能吗? - Stuart Watt
嗯,不,这不起作用。 jest.mock 被吊起所以它不在 beforeEach,所以它在一切之前运行。一个可行的选择似乎是猴子补丁 RN.Platform.OS 测试前的对象。或者将平台解耦到上下文中。 - Stuart Watt


我正在使用这个github问题的解决方案 https://github.com/facebook/jest/issues/1370#issuecomment-352597475

我从中移动了jest配置 package.json 分开文件。 到目前为止,一切似乎都很好,包括: a)根据平台导入正确的文件。例如,在ios:.ios.tsx上,然后是.native.tsx,然后是.tsx b)PLATFORM.IOS在运行test-ios时返回true,不需要模拟任何东西

// package.json
"scripts": {
  "test": "cross-env NODE_ENV=test jest --config config/jest.desktop.json",
  "test-ios": "cross-env NODE_ENV=test jest --config config/jest.ios.json",
  "test-android": "cross-env NODE_ENV=test jest --config config/jest.android.json"
}

// config/jest.web.json
{
...
}

// config/jest.ios.json
{
...
  "preset": "react-native",
  "haste": {
    "defaultPlatform": "ios",
    "platforms": [
      "android",
      "ios",
      "native"
    ],
    "providesModuleNodeModules": [
      "react-native"
    ]
  },
}

// config/jest.android.json
{
...
  "preset": "react-native",
  "haste": {
    "defaultPlatform": "android",
    "platforms": [
      "android",
      "ios",
      "native"
    ],
    "providesModuleNodeModules": [
      "react-native"
    ]
  },
}

0
2017-12-19 00:07





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();
  });
});

0
2017-08-17 04:38





可以为每个测试直接设置OS

 test('android', () => {
     Platform.OS = 'android'
     const component = renderer.create(<Component />).toJSON()
     expect(component).toMatchSnapshot()
 })
 test('ios', () => {
     Platform.OS = 'ios'
     const component = renderer.create(<Component />).toJSON()
     expect(component).toMatchSnapshot()
 })

-1
2017-10-26 17:35



没有任何解释的Downvotes对任何人都无济于事。请解释 - acharlop
测试通过但是从测试覆盖范围(分支列)似乎它没有测试两个平台 - marco