【问题标题】:How do I reset Redux between tests?如何在测试之间重置 Redux?
【发布时间】: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 };

【问题讨论】:

    标签: javascript reactjs react-native redux expo


    【解决方案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 使用“默认”值创建存储。 createMockStatecreateMockApicreateMockBle 返回每​​个相应“存储”的默认值(有“状态存储”、“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

    【讨论】:

      猜你喜欢
      • 2018-07-26
      • 2017-04-26
      • 1970-01-01
      • 2015-03-16
      • 1970-01-01
      • 2018-08-05
      • 2012-06-03
      • 2015-07-01
      • 2018-09-30
      相关资源
      最近更新 更多