react-native - 不弹出 Redux 的测试 Jest React-Native Expo CRNA

标签 react-native redux jestjs expo create-react-native-app

如何在不弹出的情况下使用 Expo(默认)为 Create React Native App (CRNA) 的组件、Redux Actions 和 Reducers 进行所有测试?

还通过 Expo 使用 Axios、Redux-Thunk 异步操作和 React-Native Maps。

最佳答案

在阅读并重新阅读 Jest 的相关文档之后, EnzymeRedux , 以及谷歌搜索特定 NPM 包版本的问题我解决了这个问题。

所有 NPM 包都必须很好地协同工作,其中有很多“事件部件”。 I.E 测试、模拟、redux 和你喜欢的 React。

这是目前有效的方法 (2018-01-16)。

设置

环境

  • OS X 高塞拉利昂
  • Visual Studio 代码

项目平台

  • 创建 React Native 应用程序 (CRNA)
  • 世博会 23.0.4
  • React 16.0.0-alpha.12
  • React-Native 0.50.3

测试框架

  • 开 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

package.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 全局配置

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 Action 创作者

这里我们在 Action 测试中使用了上面的配置。

// 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

确保 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,
            });
    });
});

关于react-native - 不弹出 Redux 的测试 Jest React-Native Expo CRNA,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48281580/

相关文章:

unit-testing - 如何修复 "TypeError: window.location is not a function"?

android - react native ! @expo/vector-icons/Fontisno 的问题

javascript - 刷新或按后退按钮后 AppBar 内容错误

javascript - 我应该在域之后还是在应用程序 View 之后构建 Redux 商店?

javascript - React-native-maps 区域未更新 - map 卡在一个区域

node.js - 从 bot.on ('send' 调用 processMessage())

reactjs - 如何测试函数是否在功能性 React 组件中调用 onChange?

react-native - 如何将谷歌支付(印度的支付应用程序)集成到本地 react 中?

ios - iOS 上 React Native 中的 BatchedBridge 错误

android - 如何在不使用任何第三方库的情况下在 React Native 项目中使用 Font Awesome?