【问题标题】:React-Redux-toolkit's: Enzyme-jest's unit testReact-Redux-toolkit 的:Enzyme-jest 的单元测试
【发布时间】:2020-11-20 00:59:23
【问题描述】:

我正在使用新的Redux toolkit。新的 Redux-toolkit 比旧的 redux 高效得多。我使用新的 Redux-toolkit 创建了多个减速器和操作。我稍微解构了 redux-toolkit 设置。我正在使用 Enzyme 和 Jest 进行单元测试。我的 redux 计数器 intialState 是 1。根据我的测试,在 it 范围内,我首先采用 intialState,然后在模拟('click')增加按钮之后,得到了我预期的结果 2。当我尝试在it 范围内测试我的减少按钮时,它会从增加的it 范围中获取结果。如果我将 intialState 1 放在减少按钮的 it 范围内,它会给我失败的测试,因为它预期为 2。我想我需要为此 counter.test 创建 mockStore。由于 redux-toolkit 的语法对我来说是新的,所以我不知道如何在 counter.test 测试套件中创建 mockstore。

附言。还有其他用于获取数据的反应组件,todolist。我也想做unit test 那些组件。如果有人帮我测试这些组件,我会非常高兴。我是单元测试的新手????。

我将代码上传到Codesandbox

下面我将解释我是如何进行设置并传递给 React 的组件的。

这是计数器减速器

import { createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  "name": `counter`,
  "initialState": 1 as number,
  "reducers": {
    "increment": (state) => state + 1,
    "decrement": (state) => state - 1
  }
});

export const { increment, decrement } = counterSlice.actions;

export default counterSlice.reducer;

这是 combineReducers

import { combineReducers } from 'redux';
import counter from 'store/reducer/counter';
import todo from 'store/reducer/todo/todo';
import fetchUser from 'store/reducer/fetch';
import fetching from 'store/reducer/createAsyncAxios';
const rootReducer = combineReducers({
  counter, //This is my counter Reducer
  todo,
  fetchUser,
  fetching
});

export type IRootState = ReturnType<typeof rootReducer>;
export default rootReducer;

这是我的商店文件

import { configureStore, Action, getDefaultMiddleware } from '@reduxjs/toolkit';
import { ThunkAction } from "redux-thunk";

import rootReducer, { IRootState } from 'store/combineReducer';


const store = configureStore({
  "reducer": rootReducer,
  "middleware": [...getDefaultMiddleware()]
  
});

export type AppThunk = ThunkAction<void, IRootState, null, Action<string>>
export default store;

附言。我析构了根文件。首先我创建了一个根文件,我在其中导入了商店,然后我连接到App。我这样做是因为我可以将根文件直接导入测试套件

import React from 'react';
import { Provider } from 'react-redux';
import store from 'store/store';
import { createGlobalStyle } from 'styled-components';

const GlobalStyle = createGlobalStyle`
  body {
    background-color: #282c34;
    color: white;
    font-family: "Lucida Console", Courier, monospace;
  }
`;

interface IProps {
  children: JSX.Element[] | JSX.Element;

}
export default ({ children }: IProps) => {
  return (
    <Provider store={store}>
      <GlobalStyle />
      {children}
    </Provider>
  );
};

这就是我的根文件连接到App的方式

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import Root from './root';

ReactDOM.render(
  <Root>
    <App />
  </Root>
  ,
  document.getElementById(`root`)
);

这是我的计数器组件

import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from 'store/reducer/counter/index';
import { IRootState } from 'store/combineReducer';
import styled from 'styled-components';
const Button = styled.button`
background-color: #4CAF50; /* Green */
  border: none;
  color: white;
  padding: 15px 32px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
`;

const Text = styled.h1`
color: blue;
`;

export default () => {
  const counter = useSelector((state: IRootState) => state.counter);

  const dispatch = useDispatch();
  return (
    <div >
      <Text>{counter}</Text>
      <Button data-test="increment" onClick={() => dispatch(increment())}>
        Increment counter
      </Button>
      <br></br>
      <br></br>
      <Button data-test="decrement" onClick={() => dispatch(decrement())}>
        Decrement counter
      </Button>
    </div>
  );
};

这是我的counter 测试套件

import React from 'react';
import { mount } from "enzyme"; // mount is full dom renderning function with children
import Counter from 'components/counter';
import Root from "root/index";

let wrapped;
beforeEach(() => {
  // I need to create mock store in here. I don't know how to do that.
  wrapped = mount(
    <Root >
      <Counter />
    </Root>
  );
  // console.log(wrapped.debug());
});

afterEach(() => {
  wrapped.unmount(); // it cleans the mount after test.
});


describe(`This is counter component`, () => {
  it(``, () => {
    expect(wrapped.find(`h1`).text()).toEqual(`1`);
  });

  it(`after click it will increase the value`, () => {
    expect(wrapped.find(`h1`).text()).toEqual(`1`);
    wrapped.find(`button`).at(0).find(`[data-test="increment"]`).simulate(`click`);
    expect(wrapped.find(`h1`).text()).toEqual(`2`);
  });
  it(`after click it will decrease the value`, () => {
    expect(wrapped.find(`h1`).text()).toEqual(`1`); // Test failed: because it Received: "2"
    wrapped.find(`button`).at(1).find(`[data-test="decrement"]`).simulate(`click`);
    expect(wrapped.find(`h1`).text()).toEqual(`2`); //
  });
});

【问题讨论】:

  • redux-mock-store 怎么样?
  • 我可以试试 :)...
  • @k-wasilewski 没有成功。感觉很蠢。
  • 我已经添加了一个答案来澄清..

标签: unit-testing jestjs enzyme redux-toolkit react-typescript


【解决方案1】:

如果我是你,我会尝试redux-mock-store,你可以在每次测试之后(或者在每次测试之前,就此而言)重置你的商店。

这是来自官方文档的示例:

// src/actions/users.test.js 
import mockAxios from "axios";
import configureMockStore from "redux-mock-store";
import thunk from "redux-thunk";
import promiseMiddleware from "redux-promise-middleware";
import { getUsers } from "./users";

const mockStore = configureMockStore([thunk, promiseMiddleware()]);

describe("User Actions", () => {
  let store;

  beforeEach(() => {
    store = mockStore({
      users: {}
    });
  });

在 beforeEach 函数中,我们重置了 store 的值,因此我们的断言中没有任何意外结果。

您必须将此模拟商店传递给您的应用,最简单的方法是将商店Provider 在您的应用中上移一级(到index.js)。然后,你会像这样模拟它:

wrapped = mount(
    <Provider store={mockStore}>
        <Root>
            <Counter />
        </Root>
    </Provider>  
);

【讨论】:

    猜你喜欢
    • 2018-12-16
    • 2018-09-14
    • 2020-09-26
    • 2019-01-27
    • 2020-03-30
    • 2019-05-06
    • 2018-08-28
    • 2019-01-29
    • 1970-01-01
    相关资源
    最近更新 更多