在未弹出 Redux 的情况下测试 Jest React-Native Expo CRNA
Posted
技术标签:
【中文标题】在未弹出 Redux 的情况下测试 Jest React-Native Expo CRNA【英文标题】:Test Jest React-Native Expo CRNA with Redux not ejected 【发布时间】:2018-06-25 04:21:06 【问题描述】:如何在未弹出的情况下使用 Expo(默认)让组件、Redux Actions 和 Reducers 的所有测试工作,以使用 Expo(默认)创建 React Native App (CRNA)?
还通过 Expo 使用 Axios、Redux-Thunk 异步操作和 React-Native Maps。
【问题讨论】:
【参考方案1】:在阅读和重新阅读Jest、Enzyme 和Redux 的相关文档之后, 以及特定 NPM 包版本的谷歌搜索问题,我对此进行了整理。
所有 NPM 包都必须协同工作,这其中有很多“可动部分”。 IE 测试、模拟、redux 和你的 React 风格。
这是目前有效的方法 (2018-01-16)。
设置
环境
OS X High Sierra Visual Studio 代码项目平台
创建 React Native 应用程序 (CRNA) 23.0.4 世博会 反应 16.0.0-alpha.12 React-Native 0.50.3测试框架
开玩笑^22.0.6 Jest-CLI ^22.0.6 Jest-酶 ^4.0.2 Jest-Expo ^22.0.0 React-addons-test-utils ^15.6.2 React-DOM 16.0.0-beta.5package.json
Redux 操作、reducer 和组件的工作测试。
"name": "MyApp",
"version": "0.0.1",
"private": true,
"author": "Thomas Hagström <thomas@crossplatform.se>",
"devDependencies":
"axios-mock-adapter": "^1.10.0",
"babel": "^6.3.26",
"babel-eslint": "^8.2.1",
"babel-jest": "^22.0.6",
"babel-polyfill": "^6.16.0",
"babel-preset-airbnb": "^1.0.1",
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
"babel-preset-react-native": "1.9.0",
"eslint": "^4.15.0",
"eslint-config-airbnb": "^16.1.0",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-react": "^7.5.1",
"jest": "^22.0.6",
"jest-cli": "^22.0.6",
"jest-enzyme": "^4.0.2",
"jest-expo": "^22.0.0",
"react-addons-test-utils": "^15.6.2",
"react-dom": "^16.0.0-beta.5",
"react-native-mock": "^0.3.1",
"react-native-scripts": "1.8.1",
"react-test-renderer": "^16.0.0-alpha.12",
"remotedev-rn-debugger": "^0.8.3"
,
"babel":
"presets": [
"es2015",
"react"
]
,
"main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
"scripts":
"start": "react-native-scripts start",
"eject": "react-native-scripts eject",
"android": "react-native-scripts android",
"ios": "react-native-scripts ios",
"test": "node node_modules/jest/bin/jest.js --watch",
"postinstall": "remotedev-debugger --hostname localhost --port 5678 --injectserver",
"eslint": "./node_modules/.bin/eslint"
,
"remotedev":
"hostname": "localhost",
"port": 5678
,
"jest":
"preset": "jest-expo",
"transformIgnorePatterns": [
"node_modules/(?!(react-native|jest-resolve|expo|lodash|enzyme|prop-types|react|jest-enzyme|enzyme|jest-expo|jest-serializer-enzyme|react-native-elements|react-native-google-places-autocomplete)/)"
],
"setupFiles": [
"./config/jest/globalFetch.js",
"./config/enzyme/index.js"
]
,
"dependencies":
"@expo/vector-icons": "^6.2.2",
"axios": "^0.17.1",
"expo": "^23.0.4",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"lodash": "^4.17.4",
"prop-types": "^15.6.0",
"react": "16.0.0-alpha.12",
"react-native": "0.50.3",
"react-native-elements": "^0.18.5",
"react-native-google-places-autocomplete": "^1.3.6",
"react-native-maps": "^0.18.0",
"react-navigation": "^1.0.0-beta.23",
"react-navigation-redux": "^0.1.0",
"react-redux": "^5.0.6",
"redux": "^3.7.2",
"redux-logger": "^3.0.6",
"redux-promise": "^0.5.3",
"redux-thunk": "^2.2.0",
"redux-mock-store": "^1.4.0",
"remote-redux-devtools": "^0.5.12",
"socketcluster-server": "^9.1.2"
酶全局配置
Enzyme 的配置脚本,见下面的package.json
,如下所示。
// config/enzyme/index.js
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
// Setup enzyme's react adapter
Enzyme.configure( adapter: new Adapter() );
示例
全球模拟
展览套件
在我的项目的根目录中,我将模拟放在__mocks__
目录中,这样它们就会被 Jest 自动拾取。
这将解决使用本地移动 API 调用的情况 - 特别是 ExpoKit SDK - 而不仅仅是 HTTP REST。
// __mocks__/expo.js
jest.mock('expo', () =>
const expo = require.requireActual('expo');
const positionMock =
latitude: 1,
longitude: 1,
;
// Mock the expo library
return
Location:
setApiKey: jest.fn(),
getCurrentPositionAsync:
options =>
new Promise(
resolve => resolve(options ?
coords: positionMock,
: null)
, null,
)
,
,
Constants:
manifest:
extra: google: maps: 'Your-API-KEY-HERE' ,
,
,
Permissions:
LOCATION: 'location',
askAsync: type => new Promise(resolve =>
resolve(type === 'location' ?
status: 'granted'
: null)),
,
...expo,
;
);
Redux - 模拟 - 商店
要使用 Thunk 配置 Redux,因此您不必在每次(操作)测试之前都这样做。在您的测试中导入 redux-mock-store
的含义将使用以下实现:
// __mocks__/redux-mock-store.js
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
export default mockStore;
常量
用作 redux 操作类型。
// src/Constants.js
const MapConstants =
MAP_LOCATION_CHANGED: 'MAP REGION CHANGED',
MAP_LOCATION_BUSY: 'MAP: GETTING LOCATION',
MAP_LOCATION_SUCCESS: 'MAP: GET LOCATION SUCCESS',
MAP_LOCATION_FAILED: 'MAP: GET LOCATION FAILED',
;
Redux 动作创建者
这里我们在动作测试中使用了上面的配置。
// src/Actions/__tests__/MapActions.test.js
import configureMockStore from 'redux-mock-store';
import MapConstants from '../../Constants';
import
GetLocation
from '../MapActions';
const store = configureMockStore();
describe('map actions', () =>
beforeEach(() =>
store.clearActions();
);
test('GetLocation returns SUCCESS when done', async () =>
const expectedPayload = latitude: 1, longitude: 1 ;
const expectedActions = [
type: MapConstants.MAP_LOCATION_BUSY ,
type: MapConstants.MAP_LOCATION_SUCCESS, payload: expectedPayload ,
];
// Dispatch action
await store.dispatch(GetLocation());
expect(store.getActions()).toMatchSnapshot();
expect(store.getActions()).toEqual(expectedActions);
);
);
组件
我使用纯组件并在单独的容器上连接我的 redux。
import React from 'react';
import shallow from 'enzyme';
import Map from '../Map';
import Colors from '../../styles';
// src/Components/__tests__/map.test.js
function setup ()
const props =
GetLocation: jest.fn(),
LocationChanged: jest.fn(),
map:
isBusy: false,
hasError: false,
errorMessage: null,
location: null,
region:
latitude: 45.52220671242907,
longitude: -122.6653281029795,
latitudeDelta: 0.04864195044303443,
longitudeDelta: 0.040142817690068,
,
,
;
const enzymeWrapper = shallow(<Map ...props />);
return
props,
enzymeWrapper,
;
describe('components', () =>
describe('Map', () =>
it('should render self and subcomponents', () =>
const enzymeWrapper = setup();
expect(enzymeWrapper).toMatchSnapshot();
const busyProps = enzymeWrapper.find('BusyIndicator').props();
expect(busyProps.isBusy).toBe(false);
expect(busyProps.loadingIndicatorColor).toEqual("#FFFFFF");
);
// TODO: Mock map functions
);
);
Redux 减速器
确保reducer返回正确的状态并且不会改变它。
import MapReducer from '../MapReducer';
import MapConstants from '../../Constants';
describe('MapReducer', () =>
test('should return the initial state', () =>
expect(MapReducer(undefined, ))
.toEqual(
isBusy: false,
hasError: false,
errorMessage: null,
location: null,
region:
latitude: 45.52220671242907,
longitude: -122.6653281029795,
latitudeDelta: 0.04864195044303443,
longitudeDelta: 0.040142817690068,
,
);
);
test(`should handle $MapConstants.MAP_LOCATION_BUSY`, () =>
expect(MapReducer(,
type: MapConstants.MAP_LOCATION_BUSY,
))
.toEqual(
hasError: false,
errorMessage: null,
isBusy: true,
type: MapConstants.MAP_LOCATION_BUSY,
);
);
test(`should handle $MapConstants.MAP_LOCATION_SUCCESS`, () =>
const resultArray = ['test'];
expect(MapReducer(,
type: MapConstants.MAP_LOCATION_SUCCESS,
payload: resultArray,
))
.toEqual(
isBusy: false,
hasError: false,
errorMessage: null,
location: resultArray,
type: MapConstants.MAP_LOCATION_SUCCESS,
);
);
test(`should handle $MapConstants.MAP_LOCATION_FAILED`, () =>
const errorString = 'test error';
expect(MapReducer(,
type: MapConstants.MAP_LOCATION_FAILED,
payload: errorString,
))
.toEqual(
isBusy: false,
hasError: true,
errorMessage: errorString,
location: null,
type: MapConstants.MAP_LOCATION_FAILED,
);
);
test(`should handle $MapConstants.MAP_LOCATION_CHANGED`, () =>
const resultArray = ['test'];
expect(MapReducer(,
type: MapConstants.MAP_LOCATION_CHANGED,
payload: resultArray,
))
.toEqual(
isBusy: false,
hasError: false,
errorMessage: null,
region: resultArray,
type: MapConstants.MAP_LOCATION_CHANGED,
);
);
);
【讨论】:
以上是关于在未弹出 Redux 的情况下测试 Jest React-Native Expo CRNA的主要内容,如果未能解决你的问题,请参考以下文章