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

Stu*_*att 19 jestjs react-native

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

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

if (Platform.OS === 'android') {
  ...
} else {
  ...
}
Run Code Online (Sandbox Code Playgroud)

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

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

Bra*_*mus 23

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

jest.mock('Platform', () => {
    const Platform = require.requireActual('Platform');
    Platform.OS = 'android';
    return Platform;
});
Run Code Online (Sandbox Code Playgroud)

把它无论是在测试的顶部,或者在一个beforeAll例如.

  • @Andru我修改了代码(所以它不再在`beforeAll`中),但这对我有用https://www.codepile.net/pile/Yr5DkK4n (5认同)
  • 在react-native 0.61之后这个解决方案不再起作用。请参阅西蒙甘布尔的答案或这些链接 https://github.com/facebook/react-native/issues/26710#issuecomment-552842415 https://altany.github.io/react-native/0.61/jest/mocking/upgrade/ 2020/01/25/mocking-react-native-0.61-modules-with-jest.html https://github.com/facebook/react-native/issues/26579#issuecomment-535244001 (4认同)
  • 任何有流量问题的人:使用`jest.requireActual`而不是`require.requireActual` (3认同)
  • 对我不起作用。有什么建议么? (2认同)
  • 模拟的缺点是您只能在一个特定平台上测试您的应用。iOS或Android。但是,如果您想测试特定组件在每个平台上的行为,该怎么办? (2认同)
  • `Object.defineProperty(Platform, 'OS', { get: jest.fn(() => 'ios') }) })` 如果一个 `beforeAll` 语句有效 (2认同)

小智 15

对于寻找此内容的每个人,它对我的​​帮助如下:

jest.mock('react-native/Libraries/Utilities/Platform', () => ({
    OS: 'android', // or 'ios'
    select: () => null
}));
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你,我没有想到要检查平台实用程序,但找不到它。对于任何尝试模拟这些模块的人,请注意,由于删除了 Haste,所以在 >0.61 中需要模拟模块的完整路径。 (3认同)

Gio*_*gio 12

我实现了一个小模拟,允许您Platform在测试期间在同一测试文件中进行更改。

将其添加到您的jest 设置文件中

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
});
Run Code Online (Sandbox Code Playgroud)

然后你就可以轻松地改变Platform你的测试了。如果您正在使用Platform.select它也将按预期工作!

import { Platform } from 'react-native'

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

describe('When iOS', () => {
  it('should ...', () => {
    Platform.OS = 'ios'
    ...
  })
})
Run Code Online (Sandbox Code Playgroud)


Boo*_*ers 8

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

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);
  });
});
Run Code Online (Sandbox Code Playgroud)

这会将平台设置为仅在测试期间在Android上运行,以确保我的函数仅调用特定的函数。包装在依赖于平台的编译中的函数如下所示:

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

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

  • 这种方法不会改变所有测试的 Platform.OS 吗? (2认同)

小智 7

React Native 0.61 更新

尽管公认的解决方案适用于 React Native 0.60 及以下版本,但 React Native 0.61 已经放弃了对 Haste 的支持,这会导致错误。

我能够按照这篇博客文章中描述的实现来模拟平台检测。

实际上,根据 React 团队的说法,我们现在必须模拟 react-native 接口。因此,您可以在react-native.js文件tests/__mocks__夹中创建一个文件并将此代码添加到模拟平台:

import * as ReactNative from "react-native";

export const Platform = {
  ...ReactNative.Platform,
  OS: "ios",
  Version: 123,
  isTesting: true,
  select: objs => objs["ios"]
};

export default Object.setPrototypeOf(
  {
    Platform
  },
  ReactNative
);
Run Code Online (Sandbox Code Playgroud)

通过这个实现,我们现在可以在运行测试之前简单地覆盖操作系统,例如:

Platform.OS = 'android'
Run Code Online (Sandbox Code Playgroud)


d0g*_*3r7 5

如果要在同一测试套件中在一个测试运行中模拟不同的操作系统则其他答案将不起作用,因此这是另一种方法。与其Platform.OS在代码中直接使用,不如在某个地方定义一个辅助函数,并使用该函数来获取对组件中操作系统的引用:

在“ helpers.js”中:

export function getOS() {
  return Platform.OS;
}
Run Code Online (Sandbox Code Playgroud)

在您的组件中:

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

此功能可以再在你的测试,如被嘲笑它

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');
  // ...
}
Run Code Online (Sandbox Code Playgroud)

这个想法归功于GitHub问题评论


Est*_*cas 5

这是你需要的模拟:

const mockPlatform = OS => {    
  jest.resetModules();  
  jest.doMock("Platform", () => ({ OS, select: objs => objs[OS] }));
};
Run Code Online (Sandbox Code Playgroud)

有了它,您可以执行以下操作:

it("my test on Android", () => {
  mockPlatform("android");
});

it("my test on iOS", () => {
  mockPlatform("ios");
});
Run Code Online (Sandbox Code Playgroud)

这样你就可以对两个平台进行测试

  • 对我不起作用。该平台不会改变默认的“iOS” (2认同)

Sim*_*ble 5

这对我有用...

jest.mock('react-native/Libraries/Utilities/Platform', () => {
  const Platform = require.requireActual(
    'react-native/Libraries/Utilities/Platform'
  )
  Platform.OS = 'android'
  return Platform
})
Run Code Online (Sandbox Code Playgroud)