javascript - 使用 saga 对组件进行 React 测试
问题描述
大家好,我在测试我的组件时遇到了一些麻烦
问题是我想测试我的React Native 组件,它使用saga从服务器获取数据。
问题是我确实知道我应该做什么,我想我应该在我的测试文件中模拟我的API调用,但我不知道如何:/
组件文件非常简单,安装后它会调度操作以获取车辆列表,然后在 UI 中显示它们。在获取之前,它会显示加载文本
下面是我当前的组件和测试文件设置。
这是一个在屏幕加载时获取初始数据的屏幕组件
屏幕组件
import React, { useContext, useEffect, useState } from 'react';
import { Platform, FlatList, View, ActivityIndicator, Text } from 'react-native';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { vehiclesActions } from '_store/vehicles';
export const MainScreen = ({ navigation }) => {
/**
* Redux selectors and dispatch
*/
const {
loading = true,
vehicles = [],
loadMore = false
} = useSelector((state) => state.vehicles);
/**
* Initial effect, fetches all vehicles
*/
useEffect(() => {
dispatch(
vehiclesActions.vehicleGet({
page: 1,
})
);
}, []);
const renderCard = () => {
return (<View><Text>Test</Text></View>)
}
if (loading) {
return (<View><Text>App Loading </Text></View>
}
return (
<View style={styles.wrapper}>
<View
style={
Platform.OS === 'ios' ? { marginTop: 30 } : { marginTop: 0, flex: 1 }
}
>
{!loading && (
<View style={Platform.OS === 'ios' ? {} : { flex: 1 }}>
<FlatList
testID={'flat-list'}
data={vehicles}
renderItem={renderCard}
/>
</View>
)}
</View>
</View>
);
};
MainScreen.propTypes = {
navigation: PropTypes.object
};
export default MainScreen;
我的车辆传奇:
const api = {
vehicles: {
getVehicles: (page) => {
return api.get(`/vehicles/list?page=${page}`, {});
},
}
function* getVehicles(action) {
try {
const { page } = action.payload;
const { data } = yield call(api.vehicles.getVehicles, page);
yield put({ type: vehiclesConstants.VEHICLE_GET_SUCCESS, payload: data });
} catch (err) {
yield call(errorHandler, err);
yield put({ type: vehiclesConstants.VEHICLE_GET_FAIL });
}
}
export function* vehiclesSaga() {
yield takeLatest(vehiclesConstants.VEHICLE_GET_REQUEST, getVehicles);
}
行动:
export const vehiclesActions = {
vehicleGet: payload => ({ type: vehiclesConstants.VEHICLE_GET_REQUEST, payload }),
vehicleGetSuccess: payload => ({ type: vehiclesConstants.VEHICLE_GET_SUCCESS, payload }),
vehicleGetFail: error => ({ type: vehiclesConstants.VEHICLE_GET_FAIL, error }),
}
减速器
import { vehiclesConstants } from "./constants";
const initialState = {
vehicles: [],
loading: true,
};
export const vehiclesReducer = (state = initialState, action) => {
switch (action.type) {
case vehiclesConstants.VEHICLE_GET_REQUEST:
return {
...state,
loading: true,
};
case vehiclesConstants.VEHICLE_GET_SUCCESS:
return {
...state,
loading: false,
vehicles: action.payload,
};
}
}
我的测试文件
import 'react-native';
import React from 'react';
import {cleanup, render, fireEvent} from '@testing-library/react-native';
import AppScreen from '../../../../src/screens/App/index';
import {Provider} from 'react-redux';
import {store} from '../../../../src/store/configureStore';
describe('App List Component', () => {
beforeEach(() => jest.useFakeTimers());
afterEach(cleanup);
it('should render vehicle list page title', async () => {
const navigation = {
setParams: () => {},
navigate: jest.fn(),
};
const route = {
}
const component = (
<Provider store={store}>
<AppScreen route={route} navigation={navigation} />
</Provider>);
const {getByText, getByTestId} = render(component);
const pageTitle = await getByText('App Loading'); // this works fine
expect(pageTitle).toBeDefined();
});
it('should navigate to add vehicle', async () => {
const navigation = {
setParams: () => {},
navigate: jest.fn(),
};
const route = {
}
const component = (
<Provider store={store}>
<AppScreen route={route} navigation={navigation} />
</Provider>);
const {getByText, getByTestId} = render(component);
const flatList = await getByTestId('flat-list');// this throws error since flat list is still not shown, and loading is showing instead
});
就像我在上面看到的那样,我找不到带有 testId flat-list 的元素,因为组件AppScreen它总是显示加载文本,有什么方法可以模拟该 API 调用并使其工作吗?
解决方案
Jest 允许您使用jest.mock
.
你必须写一个axios.get
像这样的替代方案
const vehiclesData = [
// ... put default data here
]
const delay = (ms, value) =>
new Promise(res => setTimeout(() => res(value), ms))
const mockAxiosGet = async (path) => {
let result = null
if (path.includes('vehicles/list') {
const query = new URLSearchParams(path.replace(/^[^?]+\?/, ''))
const page = + query.get('page')
const pageSize = 10
const offset = (page - 1)*pageSize
result = vehiclesData.slice(offset, offset + pageSize)
}
return delay(
// simulate 100-500ms latency
Math.floor(100 + Math.random()*400),
{ data: result }
)
}
然后将测试文件修改为
import 'react-native';
import React from 'react';
import {cleanup, render, fireEvent} from '@testing-library/react-native';
import axios from 'axios'
// enable jest mock on 'axios' module
jest.mock('axios')
import AppScreen from '../../../../src/screens/App/index';
import {Provider} from 'react-redux';
import {store} from '../../../../src/store/configureStore';
describe('App List Component', () => {
before(() => {
// mock axios implementation
axios.get.mockImplementation(mockAxiosGet)
})
beforeEach(() => jest.useFakeTimers());
afterEach(cleanup);
it('should render vehicle list page title', async () => {
const navigation = {
setParams: () => {},
navigate: jest.fn(),
};
const route = {
}
const component = (
<Provider store={store}>
<AppScreen route={route} navigation={navigation} />
</Provider>);
const {getByText, getByTestId} = render(component);
const pageTitle = await getByText('App Loading'); // this works fine
expect(pageTitle).toBeDefined();
});
it('should navigate to add vehicle', async () => {
const navigation = {
setParams: () => {},
navigate: jest.fn(),
};
const route = {
}
const component = (
<Provider store={store}>
<AppScreen route={route} navigation={navigation} />
</Provider>);
const {getByText, getByTestId} = render(component);
const flatList = await getByTestId('flat-list');// this throws error since flat list is still not shown, and loading is showing instead
});
对于您的用例,请在Mocking Implementations阅读更多内容
推荐阅读
- c# - 如何在 Unity 的 C# 中获取服务器端事件(onmessage)?
- wpf - .Net Core 3.1 WPF 中的 Prism 库
- rdf - 使用两种不同的@types 压缩 JSON-LD 属性?
- git - 如何从 Visual Studio 2019 中的提交中删除标记?
- python - 如何使用 Python 检查 2D 点是在 2D 闭合贝塞尔曲线内部还是外部?
- maven - 为 Jenkins 项目使用 Kubernetes 插件而不是管道
- directx - 低于 0.5f 的 Alpha 值是不可见的
- macos - 使用 Mac automator 按文件名合并 pdf
- powershell-5.0 - 使用 powershell 从 O365 基本身份验证切换到现代身份验证
- sql - 将多个表中的最新引用合并到共享行中的查询