Обманивание платформы в Jest и React Native
Некоторые из кода, который я пытаюсь проверить, обнаруживают платформу, используя, например:
import { Platform } from 'react-native';
...
if (Platform.OS === 'android') {
...
} else {
...
}
Есть ли разумный способ издеваться над этим с Jest и/или чем-то еще, поэтому я могу проверить обе ветки в одном тестовом прогоне?
Или это умный способ разделить его и включить платформу, например, в контекстную переменную? Хотя он всегда чувствует, что код реструктуризации, чтобы облегчить тестирование, является чем-то вроде обмана.
Ответы
Ответ 1
Это сработало для меня (Jest 21.2.1, Enzyme 3.2.0):
jest.mock('Platform', () => {
const Platform = require.requireActual('Platform');
Platform.OS = 'android';
return Platform;
});
Поместите его либо в начало вашего теста, либо в beforeAll
например.
Ответ 2
То, как я достиг высмеивания настроек платформы, просто установил его непосредственно в тестах:
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
Поскольку другие ответы не будут работать, если вы хотите смоделировать разные ОС в одном и том же наборе тестов и в одном тестовом прогоне, здесь другой способ. Вместо того, чтобы использовать Platform.OS
непосредственно в своем коде, определите где-нибудь вспомогательную функцию и используйте ее для получения ссылок на ОС в ваших компонентах:
в 'helpers.js':
export function getOS() {
return Platform.OS;
}
в вашем компоненте:
import * as helpers from './helpers';
render() {
if (helpers.getOS() === 'android') {// do something}
}
Затем эту функцию можно смоделировать в ваших тестах, например
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');
// ...
}
Автор идеи - это комментарий к проблеме GitHub.
Ответ 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");
});
Таким образом, вы можете иметь тесты для обеих платформ
Ответ 5
использовать jest.doMock и jest.resetModules
jest.resetModules()
jest.doMock('react-native', () => ({ Platform: { OS: 'android' }}))
Ответ 6
Я использую решение этой проблемы github https://github.com/facebook/jest/issues/1370#issuecomment-352597475
Я переместил jest config из package.json
для разделения файлов. Пока все работает отлично, в том числе: а) правый файл импортируется в соответствии с платформой. Например, на ios:.ios.tsx, тогда.native.tsx then.tsx b) PLATFORM.IOS возвращает true при запуске test-ios, не нужно ничего издеваться
// 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"
]
},
}
Ответ 7
Возможно, проблема в методе "импорт", проверьте это:
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);
});
Ответ 8
Вы можете издеваться над тем, что хотите от 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
});
Ответ 9
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();
});
});
Ответ 10
Я использовал beforeAll()
и afterAll()
чтобы переопределить настройку реагирования на beforeAll()
afterAll()
а затем установил его обратно, чтобы не нарушать последующие тесты.
describe('#iOS tests', () => {
beforeAll(function(){
this.os_ = Platform.OS
Platform.OS = 'ios'
});
afterAll(function(){
// Clear up my override when this group of tests have finished
Platform.OS = this.os_
});
it('should run my test here...', () => {
// ... my iOS specific test
})
})
Ответ 11
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()
})
Ответ 12
Вы должны издеваться над модулем и импортировать его в свой тест. Затем вы можете использовать mockImplementation
, чтобы установить его на любой android
или ios
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
})