使用 saga 对组件进行 React 测试
Posted
技术标签:
【中文标题】使用 saga 对组件进行 React 测试【英文标题】:React test a component with saga 【发布时间】:2021-09-17 13:25:10 【问题描述】:大家好,我在测试我的组件时遇到了一些问题
问题是我想测试我的 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 调用并让它工作?
【问题讨论】:
你用什么测试?我有使用 cypress 的经验,它拥有用于此类端到端测试的所有工具。 如果您可以在 web 模式下使用您的应用程序,您可以使用 cypress,否则请查看此***.com/q/57768294/11218031 @AvinashThakur 感谢您的回答,我正在使用 jest 和 react-native-testing 工具我只需要一种模拟 axios 调用的方法 感谢您的信息。也可以开玩笑地模拟模块。 @AvinashThakur 我只想从模块中模拟一个函数,这可能吗? 【参考方案1】: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阅读更多内容
【讨论】:
以上是关于使用 saga 对组件进行 React 测试的主要内容,如果未能解决你的问题,请参考以下文章
使用带有表单输入元素的 ReactBootstrap 模式对 React 组件进行单元测试