0

I am trying test my connected component of my React/Redux app and I wrote some test case which actually throws the error:

App component › shows account info and debits and credits`

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

The test case which trow an error app.test.js is below. And my problem is that I don't understand what should I wrap here by Connect() because I didn't use AccountInfo here:

import React from 'react';
import { mount } from 'enzyme';
import { Provider } from 'react-redux';
import App from './App';
import * as actions from '../../actions';

function setup() {
  const props = {
    errorMessage: null,
    actions
  };

  const enzymeWrapper = mount(<App {...props} />);

  return {
    props,
    enzymeWrapper,
  };
}

describe('App component', () => {
  it('shows account info and debits and credits`', () => {
    const {enzymeWrapper} = setup();
    expect(enzymeWrapper.find('.account-info').exists()).toBe(true);
    expect(enzymeWrapper.find('.debits-and-credits').exists()).toBe(true);
  });

  it('shows error message', () => {
    const {enzymeWrapper} = setup();
    enzymeWrapper.setProps({ errorMessage: 'Service Unavailable' });
    expect(enzymeWrapper.find('.error-message').exists()).toBe(true);
  });
});

My containers/app.js:

import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actions from '../actions';
import AppComponent from '../components/App/App';

const mapStateToProps = state => ({
  isFetching: state.balance.isFetching,
  errorMessage: state.errorMessage,
});

const mapDispatchToProps = dispatch => ({
  actions: bindActionCreators(actions, dispatch),
});

const AppContainer = connect(mapStateToProps, mapDispatchToProps)(AppComponent);

export default AppContainer;

The component app.js:

import React, { Component } from 'react';
import ErrorMessage from '../../containers/ErrorMessage';
import AccountInfo from '../../containers/AccountInfo';
import DebitsAndCredits from '../../containers/DebitsAndCredits';
import './App.css';

const AppComponent = () =>
  <div className="app">
    <AccountInfo />
    <DebitsAndCredits />
  </div>;

export class App extends Component {
  componentWillMount() {
    const { actions } = this.props;
    actions.fetchBalance();
  }

  render() {
    const { errorMessage } = this.props;
    return errorMessage ? <ErrorMessage /> : <AppComponent />;
  }
}

export default App;

UPD:

I updated my test case and now it looks like:

import React from 'react';
import { mount } from 'enzyme';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import createSagaMiddleware from 'redux-saga';
import { initialState } from '../../reducers/balance/balance';
import App from './App';
import * as actions from '../../actions';

const middlewares = [createSagaMiddleware];
const mockStore = configureMockStore(middlewares);
const store = mockStore(initialState);

function setup() {
  const props = {
    errorMessage: null,
    actions,
  };

  const enzymeWrapper = mount(
    <Provider store={store}>
      <App {...props} />
    </Provider>
  );

  return {
    props,
    enzymeWrapper,
  };
}

describe('App component', () => {
  it('shows account info and debits and credits`', () => {
    const { enzymeWrapper } = setup();
    expect(enzymeWrapper.find('.account-info').exists()).toBe(true);
    expect(enzymeWrapper.find('.debits-and-credits').exists()).toBe(true);
  });

  it('shows error message', () => {
    const { enzymeWrapper } = setup();
    enzymeWrapper.setProps({ errorMessage: 'Service Unavailable' });
    expect(enzymeWrapper.find('.error-message').exists()).toBe(true);
  });
});

And my error now is:

App component › shows account info and debits and credits`

    TypeError: Cannot read property 'account' of undefined

UPD 2:

My initialState which I put when I create mocked store:

const initialState = {
  isFetching: false,
  account: {},
  currency: '',
  debitsAndCredits: [],
};

My AccountInfo component:

import React from 'react';

const AccountInfo = ({ account, currency }) =>
  <header className="account-info">
    <p>{account.name}</p>
    <p>
      IBAN: {account.iban}<br />
      Balance: {account.balance}<br />
      Currency: {currency}<br />
    </p>
  </header>;

export default AccountInfo;
rel1x
  • 2,351
  • 4
  • 34
  • 62

1 Answers1

2

For testing the connected component, you need to mock the provider as well, since the connect picks state variables from redux store.

Do this

const enzymeWrapper = mount (<Provider store={mockStore}><App {...props}/></Provider>)

You need to mock the redux store too.

Edit 1:

Just looking at your AccountInfo component it tells me that you are expecting account in the props here.

AccountInfo = ({account}) =>

So that means App.js has to pass down the accounts' value in the props. Same thing goes for currency.

Aftab Khan
  • 3,853
  • 1
  • 21
  • 30
  • Thanks a lot! I mocked store and maybe I did it using wrong way, can you take another one look at my post (i updated it), please. – rel1x Jun 22 '17 at 20:07
  • Can you post what is your initial state that you are importing? – Aftab Khan Jun 23 '17 at 05:52
  • Also I need the code for AccountInfo container. I'm guessing that's where account is trying to be read. – Aftab Khan Jun 23 '17 at 06:02
  • On second thoughts, instead of posting it can you try to figure out which component is trying to look for "account" variable and make sure it's included in mocked store – Aftab Khan Jun 23 '17 at 06:02
  • Updated my post again. Thanks for trying to help me! – rel1x Jun 23 '17 at 06:17
  • I want you to traverse your component hierarchy and figure out what all needs to be passed – Aftab Khan Jun 23 '17 at 06:27
  • Ok, but how to pass in my case the arguments for all children components. And I didn't pass any arguments in my component `app.js` so why I should pass them in the tests? – rel1x Jun 23 '17 at 09:24
  • I mean I have no place to pass arguments for children components in my `app.js`. – rel1x Jun 23 '17 at 09:24
  • Run this code normally also, I think it should throw the same error. If you can make a small fiddle and put relevant stuff there. I can make edits in that – Aftab Khan Jun 23 '17 at 11:04