首页 > 解决方案 > 不变违规:在“连接(AddTodo)”的上下文中找不到“商店”。执行 npm 测试时

问题描述

https://github.com/RitikPatni/react-todo

运行“npm test”时,我得到以下信息:

不变违规:在“Connect(AddTodo)”的上下文中找不到“store”。要么将根组件包装在 a 中,要么将自定义的 React 上下文提供者传递给连接选项中的 Connect(AddTodo),并将相应的 React 上下文消费者传递给 Connect(AddTodo)。

我很确定它来自 components/addTodo.js 内的行:

export default connect()(AddTodo);

但是我不确定我应该修改什么才能使测试成功运行而不会破坏应用程序。

这是失败的测试,它位于 App.test.js 中:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

it('renders without crashing', () => {
  const div = document.createElement('div');
  ReactDOM.render(<App />, div);
  ReactDOM.unmountComponentAtNode(div);
});

在阅读了 Drew Reese 的出色回答后,我尝试了这个:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import todo from './reducers/todo'

it('renders without crashing', () => {
  const div = document.createElement('div');
  ReactDOM.render(
    <Provider store={createStore(todo, [{
      id: 1,
      dueDate: new Date('July 22, 2018 07:22:13').toDateString(),
      completed: false,
      text: 'Run the tests',
      deleted: false,
  }])}>
      <App />
    </Provider>,
    div
  );
  ReactDOM.unmountComponentAtNode(div);
});

但是,我得到:

 FAIL  src/App.test.js
  ● Console

    console.log src/reducers/todo.js:2
      { type: '@@redux/INIT1.z.e.5.x' }
    console.error node_modules/react-dom/cjs/react-dom.development.js:530
      Warning: Invalid DOM property `class`. Did you mean `className`?
          in span (at addTodo.js:38)
          in form (at addTodo.js:10)
          in div (at addTodo.js:9)
          in AddTodo (created by Context.Consumer)
          in Connect(AddTodo) (at App.js:7)
          in div (at App.js:5)
          in App (at App.test.js:18)
          in Provider (at App.test.js:11)
    console.error node_modules/prop-types/checkPropTypes.js:20
      Warning: Failed prop type: The prop `todos` is marked as required in `TodoList`, but its value is `undefined`.
          in TodoList (created by Context.Consumer)
          in Connect(TodoList) (at App.js:8)
          in div (at App.js:5)
          in App (at App.test.js:18)
          in Provider (at App.test.js:11)
    console.error node_modules/prop-types/checkPropTypes.js:20
      Warning: Failed prop type: The prop `toggleTodo` is marked as required in `TodoList`, but its value is `undefined`.
          in TodoList (created by Context.Consumer)
          in Connect(TodoList) (at App.js:8)
          in div (at App.js:5)
          in App (at App.test.js:18)
          in Provider (at App.test.js:11)
    console.error node_modules/prop-types/checkPropTypes.js:20
      Warning: Failed prop type: The prop `removeTodo` is marked as required in `TodoList`, but its value is `undefined`.
          in TodoList (created by Context.Consumer)
          in Connect(TodoList) (at App.js:8)
          in div (at App.js:5)
          in App (at App.test.js:18)
          in Provider (at App.test.js:11)
    console.error node_modules/react-dom/cjs/react-dom.development.js:21843
      The above error occurred in the <Context.Consumer> component:
          in Connect(TodoList) (at App.js:8)
          in div (at App.js:5)
          in App (at App.test.js:18)
          in Provider (at App.test.js:11)

      Consider adding an error boundary to your tree to customize error handling behavior.
      Visit https://reactjs.org/docs/error-boundaries.html to learn more about error boundaries.

  ● renders without crashing

    Unknown filter: undefined

       8 |       return todos;
       9 |     default:
    > 10 |       throw new Error('Unknown filter: ' + filter);
         |             ^
      11 |   }
      12 | };
      13 | 

      at getVisibleTodos (src/components/visibleTodoList.js:10:13)
      at Function.mapStateToProps [as mapToProps] (src/components/visibleTodoList.js:16:12)
      at mapToPropsProxy (node_modules/react-redux/lib/connect/wrapMapToProps.js:53:92)
      at Function.detectFactoryAndVerify (node_modules/react-redux/lib/connect/wrapMapToProps.js:62:19)
      at mapToPropsProxy (node_modules/react-redux/lib/connect/wrapMapToProps.js:53:46)
      at handleFirstCall (node_modules/react-redux/lib/connect/selectorFactory.js:34:18)
      at pureFinalPropsSelector (node_modules/react-redux/lib/connect/selectorFactory.js:75:81)
      at Connect.selectDerivedProps (node_modules/react-redux/lib/components/connectAdvanced.js:126:25)
      at Connect.renderWrappedComponent (node_modules/react-redux/lib/components/connectAdvanced.js:183:33)
      at Connect.indirectRenderWrappedComponent (node_modules/react-redux/lib/components/connectAdvanced.js:168:21)
      at updateContextConsumer (node_modules/react-dom/cjs/react-dom.development.js:19844:19)
      at beginWork$1 (node_modules/react-dom/cjs/react-dom.development.js:20227:14)
      at beginWork$$1 (node_modules/react-dom/cjs/react-dom.development.js:25756:14)
      at performUnitOfWork (node_modules/react-dom/cjs/react-dom.development.js:24698:12)
      at workLoopSync (node_modules/react-dom/cjs/react-dom.development.js:24671:22)
      at performSyncWorkOnRoot (node_modules/react-dom/cjs/react-dom.development.js:24270:11)
      at scheduleUpdateOnFiber (node_modules/react-dom/cjs/react-dom.development.js:23698:7)
      at updateContainer (node_modules/react-dom/cjs/react-dom.development.js:27103:3)
      at node_modules/react-dom/cjs/react-dom.development.js:27528:7
      at unbatchedUpdates (node_modules/react-dom/cjs/react-dom.development.js:24433:12)
      at legacyRenderSubtreeIntoContainer (node_modules/react-dom/cjs/react-dom.development.js:27527:5)
      at Object.render (node_modules/react-dom/cjs/react-dom.development.js:27608:10)
      at Object.<anonymous>.it (src/App.test.js:10:12)

试过:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import todo from './reducers/todo'
import rootReducer from './reducers/';

const store = createStore(rootReducer);


it('renders without crashing', () => {
  const div = document.createElement('div');
  ReactDOM.render(
    <Provider store={store}>
      <App />
    </Provider>,
    div
  );
  ReactDOM.unmountComponentAtNode(div);
});

现在测试通过了。不确定是否可以通过 rootReducer 存储。

标签: reactjsunit-testingjestjs

解决方案


通常的模式是还导出“未连接”组件以进行测试并手动传递连接HOC(Higer Order 组件)提供的道具(从状态映射),这样您就不必模拟 redux 存储提供程序或创建用于测试的包装器。

export const MyComponent = ({ myProp }) => {
  return (<div>{myProp}</div>);
};

const mapStateToProps = state => ({ myProp: state.someStateValue });

export default connect(mapStateToProps)(MyComponent);

测试 MyComponent

// all the other testing imports
import { MyComponent } from './MyComponent'; // notice using named unconnected component, not the default export

it('renders without crashing', () => {
  // Something like this
  // render(<MyComponent myProp={ /* some value for testing */} />);
});

在某些情况下,如果您正在测试一个呈现连接组件的组件,那么您需要提供一个包装器,为需要它的组件提供上下文。

在您的应用测试中,您需要使用提供商店的包装器进行渲染:

import { createStore } from 'redux;
import { Provider } from 'react-redux';

it('renders without crashing', () => {
  const div = document.createElement('div');
  ReactDOM.render(
    <Provider store={createStore(reducer, initialState)}>
      <App />
    </Provider>,
    div
  );
  ReactDOM.unmountComponentAtNode(div);
});

推荐阅读