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

时间:2017-04-01 20:02:50

标签: react-native jestjs

我尝试测试的一些代码使用例如:

来检测平台
import { Platform } from 'react-native';
...

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

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

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

17 个答案:

答案 0 :(得分:18)

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

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

将其放在测试的顶部,或者放在a beforeAll中。

答案 1 :(得分:6)

反应Native 0.61更新

尽管可接受的解决方案适用于React Native 0.60及更低版本,但是React Native 0.61放弃了对Haste的支持,这会导致错误。

按照此blog post中所述的实现,我能够模拟平台检测。

实际上,according to the React team,我们现在必须模拟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
);

通过这种实现,我们现在可以像执行以下测试一样简单地覆盖操作系统:

Platform.OS = 'android'

答案 2 :(得分:5)

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

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,因为理想情况下,一个功能应该只有一个责任。但是,我确信您可以使用它来运行第一个测试,动态设置平台并在一个函数中运行第二个测试。

答案 3 :(得分:4)

这是您需要的模拟:

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

使用它可以执行以下操作:

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

it("my test on iOS", () => {
  mockPlatform("ios");
});

这样,您就可以在两个平台上进行测试

答案 4 :(得分:3)

如果你想在同一个测试套件和中模拟不同的操作系统,那么其他答案将不起作用,这是另一种方法。不要在代码中直接使用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');
  // ...
}

这个想法归功于this GitHub issue comment

答案 5 :(得分:3)

这对我有用...

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

答案 6 :(得分:1)

使用jest.doMockjest.resetModules

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

答案 7 :(得分:1)

我正在使用此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"
    ]
  },
}

答案 8 :(得分:1)

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

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

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

使用“导入”功能将无法使用,需要使用“要求”。

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

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

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

答案 9 :(得分:1)

对于较新版本

“ react-native”:“ 0.62.2”

“酶”:“ ^ 3.11.0”

“笑话”:“ 24.5.0”

将其放在测试的顶部

Object.defineProperty(Platform, 'OS', { get: jest.fn(() => 'ios') })

答案 10 :(得分:0)

我实现了一个小模拟程序,使您可以在同一测试文件中的测试过程中更改Platform

将此添加到您的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
});

然后,您可以在测试中轻松更改Platform。如果您使用的是Platform.select,它也将按预期运行!

import { Platform } from 'react-native'

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

describe('When iOS', () => {
  it('should ...', () => {
    Platform.OS = 'ios'
    ...
  })
})

答案 11 :(得分:0)

你可以像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
  });

答案 12 :(得分:0)

jest: ^26.5.3

请参阅此article

的底部
import { Platform } from 'react-native';

describe('Android', () => {
  it('renders Element if Android', () => {
    Platform.OS = 'android';
    renderIfAndroid();
    expect(wrapper.find(Element)).exists()).toBe(true);
  });
});

describe('IOS', () => {
  it('renders Element if IOS', () => {
    Platform.OS = 'ios';
    renderIfIOS();
    expect(wrapper.find(Element)).exists()).toBe(true);
  });
});

答案 13 :(得分:0)

如果有人在嘲笑A[2:]。以下代码可以解决您的问题。

A[:2]

并同时嘲笑BPlatform.select。请参考下面的代码。

const mockedData = 'MOCKED-DATA'
jest.mock('react-native', () => ({

    Platform: {
        select: jest.fn(() => {
            return { mockedData  } // Your Mocked Value
        }),
    }
}));

答案 14 :(得分:0)

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

答案 15 :(得分:-1)

可以为每个测试直接设置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()
 })

答案 16 :(得分:-2)

您必须模拟模块并将其导入测试。然后,您可以使用mockImplementation将其设置为androidios

import reactNative from 'react-native';
jest.mock('react-native', () = > jest.fn();

it('is android', () => {
  reactNative.mockImplementation(()=>({Platform:{OS: 'android'}}))
  //test the android case
})

it('is android', ()=>{
  reactNative.mockImplementation(()=>({Platform: { OS: 'io' }}))
  //test the ios case
})