【问题标题】:Redux How to update the store in unit tests?Redux 如何在单元测试中更新存储?
【发布时间】:2017-05-29 06:20:17
【问题描述】:

使用酶、摩卡和期望断言。

我的单元测试的目的是检查在 mergeProps 中暂停和未暂停时,调度是否使用正确的参数调用。 我需要动态更改商店的状态:paused: true

目前我尝试通过调度更新暂停的值,但我认为这是不正确的,因为它只是一个模拟并且从未真正通过减速器运行。

我正在使用包redux-mock-store

我该怎么做?

describe('Play Container', () => {
  const id = 'audio-player-1';

  const store = configureMockStore()({
    players: {
        'audio-player-1': { paused: false }
    }
  });
  let dispatchSpy;
  let wrapper;

  beforeEach(() => {
    dispatchSpy = expect.spyOn(store, 'dispatch');
    wrapper = shallow(
      <PlayContainer className={attributes.className}>
        {children}
      </PlayContainer>,
      { context: { id } },
      ).shallow({ context: { store } });
  });

  it('onClick toggles play if paused', () => {
    //Not Working
    store.dispatch(updateOption('paused', true, id));
    wrapper.simulate('click');
    expect(dispatchSpy).toHaveBeenCalledWith(play(id));
  });

  it('onClick toggles pause if playing', () => {
    wrapper.simulate('click');
    expect(dispatchSpy).toHaveBeenCalledWith(pause(id));
  });
});

容器:

const mapStateToProps = ({ players }, { id }) => ({
  paused: players[id].paused
});

const mergeProps = (stateProps, { dispatch }, { id }) => ({
  onClick: () => (stateProps.paused ? dispatch(play(id)) : dispatch(pause(id)))
});

export default connectWithId(mapStateToProps, null, mergeProps)(Play);

connectWithId:

//getContext() is from recompose library and just injects id into props
export const connectWithId = (...args) => compose(
  getContext({ id: React.PropTypes.string }),
  connect(...args),
);

行动:

updateOption: (key, value, id) => ({
    type: actionTypes.player.UPDATE_OPTION,
    key,
    value,
    id,
}),

【问题讨论】:

  • 你看过这个链接吗? redux.js.org/docs/recipes/WritingTests.html
  • 是的,我还建议阅读上述文档。你应该分别测试三个不同的东西:actions、reducers、components。在测试动作(实际上是动作创建者)时,您只想确保调度的实际动作正确形成。在测试 reducer 时,您希望像您已经在做的那样模拟 store,调度一些操作,并确保返回的新状态是您所期望的。在测试组件时,您希望实际测试行为(即,如果我单击此处,则调用 dispatch),当然要模拟 dispatch。

标签: unit-testing reactjs redux mocha.js enzyme


【解决方案1】:

configureMockStore 是一个工厂,用于通过应用指定的中间件来配置模拟存储。这个工厂返回一个mockStore 函数。

mockStore 函数本身返回配置的模拟存储的实例。它不会通过动作改变状态;相反,它只记录通过了哪些操作。这是因为它是一个用于创建单元测试和不是“集成”(状态+组件)测试的实用工具。

尽管如此,您可以模拟状态变化。 mockStore 接受一个函数,因此您可以执行以下操作:

import configureMockStore from 'redux-mock-store';

const middlewares = [];
const mockStore = configureMockStore(middlewares);

let state = {
  players: {
    'audio-player-1': { paused: false }
  }
};

const store = mockStore(() => state);

然后在你的测试中,你可以这样做:

state = NEW_STATE;

// now you need to make the components update the state.
// so you can dispatch any action so the mock store will notify the subscribers
store.dispatch({ type: 'ANY_ACTION' }); 

【讨论】:

  • 使用返回模拟状态的函数初始化模拟存储非常重要,如上所述,const store = mockStore(() =&gt; state); 并且需要覆盖状态,仅更改属性将不起作用
【解决方案2】:

您可以做的是在测试中使用真实的商店。首先,创建一个reducer函数:

const reducer = (state, action) => {
  if (action.type === actionTypes.player.UPDATE_OPTION) {
    return {
      ...state,
      players: {
        ...state.players,
        [action.id]: {
          ...state.players[action.id],
          [action.key]: action.value,
        },
      },
    };
  }
  return state;
};

(注意,如果您可以在此测试中不保留其他状态,您可以简化上述内容并返回一个新状态。)

然后使用该 reducer 和初始状态创建一个商店:

import { createStore } from 'redux';

const store = createStore(reducer, {
  players: {
    'audio-player-1': { paused: false }
  }
});

这样,您与updateOption 的调度应该会导致新状态。

【讨论】:

    【解决方案3】:

    现在看来,感谢pr by @catarinasoliveira,您可以提供真正的减速器,它将相应地更新商店。它在 master 中,但我不知道它是否在 npm 中,或者坦率地说,它是否符合我刚才所说的那样,但我将尝试一下,并会报告

    【讨论】:

      猜你喜欢
      • 2020-01-09
      • 2018-10-12
      • 1970-01-01
      • 1970-01-01
      • 2012-03-01
      • 2020-09-14
      • 1970-01-01
      • 2019-01-29
      • 1970-01-01
      相关资源
      最近更新 更多