2

I have a smart component, trying to write unit test (DOM tests) - getting the following error: Not sure why I am getting this error even though I am passing props in the test..?

Invariant Violation: Could not find "store" in either the context or props of "Connect(myComponent)". Either wrap the root component in a , or explicitly pass "store" as a prop to "Connect(myComponent)".

Updated New error: TypeError: Cannot read property 'mainData' of undefined at mapStateToProps

test code:

import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import { renderComponent, expect } from '../../test_helper';
import myComponent from '../../../src/containers/myComponent';


describe('myComponent', () => {
  const mockStore = configureMockStore();
  let connectedApp,
    store,
    initialItems;
  let component;
  let componentRender;

  beforeEach(() => {
    const DataMock = [1, 2, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 
      0, 1, 2, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 1, 
      2, 0, 0, 0, 0, 0];

    const props = {
      mainData: DataMock,
      abc: 'def',
      subData: {
        test: '1',
        testing: 12,
      },
      userID: '1',
      Date: 'jan11',
    };

    initialItems = ['one'];
    const initialState = {
      items: initialItems
    };

    store = mockStore(initialState);

    component = TestUtils.renderIntoDocument(
      <Provider store={store}><myComponent {...props} /></Provider>);

    componentRender = ReactDOM.findDOMNode(component);
  });

  it('loads', () => {
    expect(component).to.exist;
  });
});

this myComponent is a child of a dumb component

myComponent code:

import React, { PropTypes, Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../actions/component_actions';

class myComponent extends Component {

  constructor(props) {
    super(props);
    this.state = {
      someString: 'abc',
      someotherstring: 'def',
    };
  }

  componentDidMount() {
    const { test1, test2, test3 } = this.props;
    this.props.fetchEntriesDefault(test1, test2, test3);
    this.props.fetchAnalyticsMainView(test1, test2, test3);
  }

render() {
    return (
      <div className="container">
     </div>
   );
  }
 }


    function mapStateToProps(state) {
      return {
        mainData: state.reducerName.mainData,
        subDataData: state.reducerName.subDataData,
      };
    }
    myComponent.propTypes = {
      mainData: PropTypes.array,
      abc: PropTypes.string,
      subDataData: PropTypes.object,
      userID: PropTypes.string,
      actioncreatorfuncone: PropTypes.func,
      actioncreatorfunctwo: PropTypes.func,
      date: PropTypes.string,
    };

    export default connect(mapStateToProps, actions)(myComponent);
monkeyjs
  • 604
  • 2
  • 7
  • 29

2 Answers2

2

The error clearly states that, the MyComponent is connected to the store using connect from redux. Hence when you use the TestUtils.renderIntoDocument(<myComponent {...props} />);

The component tries to use redux to fetch the store that needs to be supplied. You need to create a test store in order for your connected component to receive reducer.

Example:

import React from 'react';
import ReactDOM from 'react-dom';

// IMPORTANT
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';

import TestUtils from 'react-addons-test-utils';
import { renderComponent, expect } from '../../test_helper';
import MyComponent from '../../../src/containers/myComponent';    

describe('myComponent', () => {
  var mockStore = configureMockStore();
  var connectedApp, store, initialItems;
  let component;
  let componentRender;

  beforeEach(() => {
    const DataMock = [1, 2, 0, 0, 0, 0, 0, 1, 2, 0, 0, 
      0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 
      0, 1, 2, 0, 0, 0, 0, 0
    ];

    const initialState = {
      mainData: DataMock,
      userID: '1'
    };

    store = mockStore(initialState);
  });

  describe('state provided by the store', function() {
    beforeEach(function() {

      component = TestUtils.renderIntoDocument(
        <Provider store={store}><MyComponent /></Provider>);

      componentRender = ReactDOM.findDOMNode(component);
    });

    it('loads', () => {
      expect(component).to.exist;
    });
  });
});

In this way, you need to add the provider with the store for a connected component with redux.

Update The Actions may not have an undefined "type" property is due to the fact that, in myComponent the connect method is not getting actions. Could you please post your entire code for myComponent? The actions that needs to be added? The actions should be a dictionary like:

Something like this example:

import { connect } from 'react-redux'
import { login } from '../actions/creators/userActionCreators'

function mapStateToProps(state) {
  return {
    mainData: state.reducerName.mainData,
    subDataData: state.reducerName.subDataData,
  };
}

const mapDispatchToProps = (dispatch) => {
   return {
      onSubmitLogin: (id, pass) => dispatch(login(id, pass))
   }
};

// `LoginForm` is being passed, so it would be the "container"
// component in this scenario
export default connect(mapStateToProps, mapDispatchToProps)(myComponent);
Nagaraj Tantri
  • 5,172
  • 12
  • 54
  • 78
  • Thanks for your help, updated my code but I get a new error - I have updated my question with the latest code - TypeError: Cannot read property 'mainData' of undefined, mainData is the exact word I am using in my component - am I passing the props right ? @ http://stackoverflow.com/users/308565/nagaraj-tantri – monkeyjs Jan 02 '17 at 21:27
  • Updated the answer, since you need the `mainData` from redux state, you cannot pass it as `props`, you need to initialize it in `mockStore` like: `const initialState = { mainData: DataMock, userID: '1' };` and then `store = mockStore(initialState);` – Nagaraj Tantri Jan 03 '17 at 01:43
  • tried it but same error - I have just updated the code of myComponent - mainData: state.reducerName.mainData, is the issue related to reducername here? because the error says mainData of undefined in component - how to handle this from test case? http://stackoverflow.com/users/308565/nagaraj-tantri – monkeyjs Jan 03 '17 at 02:09
  • @monkeyjs yes, its because, the object deep nest. Now, if your `mainData` is inside `reducerName` then ensure your test data is also the similar key value structure. So, `const initialState = { reducerName: { mainData: DataMock } };` – Nagaraj Tantri Jan 03 '17 at 03:08
  • I am getting Error: Actions may not have an undefined "type" property. Have you misspelled a constant? Action: undefined at dispatch (node_modules/redux-mock-store/lib/index.js:39:19) at Object. actioncreatorfuncone (node_modules/redux/lib/bindActionCreators.js:7:12) - should I be doing something like this - const action = { types: 'ADD_ITEM' } – monkeyjs Jan 03 '17 at 04:07
  • @monkeyjs nope, the `MyComponent` takes second parameter as `actions`, so it's because the mock store is not knowing the actions that are need to be specified. whenever you hit such roadblocks, use `debugger` inside the code and use chrome developers tools to see where is the data getting corrupted :) – Nagaraj Tantri Jan 03 '17 at 04:19
  • something like this - ); ? – monkeyjs Jan 03 '17 at 04:55
  • @monkeyjs no, actions are defined in your component and not in test and hence not in ` – Nagaraj Tantri Jan 03 '17 at 05:41
  • added the complete code of myComponent - ignore string name discrepancies but that is the whole structure how I have actions within the connect -http://stackoverflow.com/users/308565/nagaraj-tantri – monkeyjs Jan 03 '17 at 18:21
  • @monkeyjs `import * as actions from '../../actions/component_actions';` is not giving the actions to connect method. Thats why you are getting `Action: undefined at dispatch...`. The actions should be an object of functions that return pure objects. – Nagaraj Tantri Jan 04 '17 at 13:40
1

One of the way I am doing is exporting the component separately just for testing as below.

export class MyComponent extends Component {
  // your stuff here
} 

export default connect(mapStateToProps, actions)(MyComponent);

Here we will import component without redux wrapper for testing as

import { MyComponent } from '../../../src/containers/myComponent'; 

For reference Testing Redux Component

Note: we will have to pass required props to the component.

duwalanise
  • 1,312
  • 1
  • 14
  • 24