如何在测试之间重置 Redux?
Posted
技术标签:
【中文标题】如何在测试之间重置 Redux?【英文标题】:How do I reset Redux between tests? 【发布时间】:2022-01-04 18:43:54 【问题描述】:有没有人能够在测试之间清除 Redux?你做了什么?
我有一个使用 Redux 的 React Native Expo 应用程序。
我有一个单独运行的测试,但是当我运行所有测试时,它失败了,因为其他测试在 redux 中留下了状态更改。
如何在测试之间“刷新”redux 存储。
我尝试了 mock-redux-store 包,但是当我将 combineReducers() 传递给它时,它什么也没返回。
我可以在那里手动重建状态切片,并将它们传递给模拟存储,但这很难维护。
testing-library-utils.js
:
//This file adds a Redux Provider to a component's context when running Jest tests from test files.
//This first import enables jest native, which enables some state-centric matchers
import '@testing-library/jest-native/extend-expect';
import React from 'react';
import render from '@testing-library/react-native';
import Provider, combineReducers, applyMiddleware, compose from 'redux';
import ReduxThunk from 'redux-thunk';
import createMockStore from 'redux-test-utils';
// import store from '../App';
import logBooksReducer from '../store/reducers/logBooks-reducer';
import authReducer from '../store/reducers/auth-reducer';
const composedMiddleWare = compose(applyMiddleware(ReduxThunk));
const rootReducer = combineReducers(
logBooks: logBooksReducer,
auth: authReducer,
);
const store = createMockStore(rootReducer, composedMiddleWare);
console.log('STORE ', JSON.stringify(store.getState()));
const AllTheProviders = ( children ) =>
console.log('Store.getState() from UTILS ', store.getState());
return <Provider store=store>children</Provider>;
;
const customRender = (ui, options) =>
render(ui, wrapper: AllTheProviders, ...options );
// re-export everything
export * from '@testing-library/react-native';
// override render method
export customRender as render ;
【问题讨论】:
【参考方案1】:Redux 是一个定义应用程序当前状态的对象。因此,除非您知道笑话的正确顺序,否则它不会起作用。顺便说一句,Jest 并不是每次都以相同的顺序运行测试。有时它会存储失败的那些并在下一次运行它们。
建议您为每个测试/测试文件升级 Redux 存储。
这是我的test/utils/mocks/redux/index.js
import configureStore from 'redux-mock-store';
import createMockBluetooth from '../bt';
import createMockApi from '../api';
import createMockState from './state';
...
const createMockStore = (
state = createMockState(),
api = createMockApi(),
ble = createMockBluetooth(),
...,
middlewares = [],
= ) =>
const thunkMiddleware = ( dispatch, getState ) => (next) => (action) => (
typeof action !== 'function' ?
next(action) :
action(dispatch, getState, api, bt, ... )
);
return configureStore([thunkMiddleware, ...middlewares])(state);
;
export default createMockStore;
这样我就可以为每个测试定义不同的商店:
addressList.test.js
const defaultProps =
navigation:
state: params: ,
dispatch: jest.fn(),
...
pop: jest.fn(),
popToTop: jest.fn(),
isFocused: jest.fn(),
,
;
const getStoreWithMockAddresses = (mockData) =>
createMockStore(
state: createMockState(
listOfAddresses: mockData,
,
customMerge: preferOverride('listOfAddresses'),
),
);
....
test('random test', () =>
const mockAddresses =
fullList: [
name: 'address-1', number: 69 ,
],
;
const mockStore = getStoreWithMockAddresses(mockAddresses);
const getByTestId = render(<YourComponent navigation= defaultProps.navigation />, wrapper: store: mockStore );
我确实需要为每个测试进行设置。这是有道理的,因为我正在测试组件将在特定状态下工作。
请注意,render
方法语法有点像,因为我使用的是原始渲染的自定义实现。
编辑:
import merge from 'deepmerge';
const createMockState = (overrides = , options = []) => merge(
...
listOfAddresses:
fullList: [] ,
, overrides, options);
export default createMockState;
【讨论】:
谢谢!你能帮我理解你的部分代码吗?我看到在上面的 redux 模拟文件中,您从“./state”中引入了状态切片,但是在测试文件中,您再次定义了状态切片。我对定义状态切片/模型的位置感到困惑。你能帮我澄清一下吗?createMockStore
使用“默认”值创建存储。 createMockState
、 createMockApi
和 createMockBle
返回每个相应“存储”的默认值(有“状态存储”、“api 存储”、“ble 存储”等)。对于每个测试,我将覆盖createMockState
给出的默认值,因为我想要当 store.state.listOfAddresses.fullList 具有该条目时组件/应用程序的行为方式。 (默认情况下,createMockStore
返回一个空数组。
我在原始答案中添加createMockStore
的代码
再次感谢。我有点难以跟上。不是你的错,我只是模拟新手,我在单独的“创建”文件中没有我的初始状态。我喜欢你的方法如何从头开始创建一个商店,里面有状态。有没有你知道的我可以一步一步学习的地方?
它也已经存在了。【参考方案2】:
终于明白了!
这是我对Redux Testing Docs中内容的修改版
//This file adds a Redux Provider to a component's context when running Jest tests from test files.
//This first import enables jest native, which enables some state-centric matchers
import '@testing-library/jest-native/extend-expect';
import React from 'react';
import render as rtlRender from '@testing-library/react-native'; // This line is important
import Provider from 'react-redux';
import rootReducer from '../App'; //Importing my same Root Reducer, so maintaining is easier
import NavigationContainer from '@react-navigation/native';
import configureStore from '@reduxjs/toolkit'; //Need to install @reduxjs/toolkit
const render = (
ui,
preloadedState,
store = configureStore(
reducer: rootReducer,
preloadedState,
middleware: getDefaultMiddleware =>
getDefaultMiddleware(
serializableCheck: false,
),
),
...renderOptions
=
) =>
const Wrapper = ( children ) =>
return (
<Provider store=store>
<NavigationContainer>children</NavigationContainer> //Adding nav container here
</Provider>
);
;
return rtlRender(ui, wrapper: Wrapper, ...renderOptions );
;
// re-export everything
export * from '@testing-library/react-native';
// override render method
export render ;
然后,在每个测试的基础上,您将预加载状态传递给测试的渲染函数。这样每个测试都可以有一个单独的 redux 测试环境。
it('Displays the locaiton', async () =>
const preloadedState = entries: allEntries: myPreloadedStateForThisTest ; // Make sure to match the data structure of your real state.
const findByText = render(<EntriesScreen />, preloadedState );
// Expect:
await findByText(/West Virginia/i);
);
感谢@markerikson 提供examlple code
【讨论】:
以上是关于如何在测试之间重置 Redux?的主要内容,如果未能解决你的问题,请参考以下文章
使用 @reduxjs/toolkit 中的 configureStore 时如何重置 Redux Store 的状态?