4

I am trying to test an input component in my React-Redux app and trying to create a mock of my redux store with 'redux-mock-store'.

When I try to run the test I get "Cannot read property 'getState' of undefined" error, so I guess I'm not initializing my mock store correctly but I don't know what I'm doing wrong.

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import InputField from './InputField';
import configureStore from 'redux-mock-store';

describe("<InputField />", () => {
    const mockStore = configureStore([]);

//these are my two reducers as defined in my real redux store, so I'm passing them to the mock as well
    const chosenCityReducer = (chosenCity = null, action) => {
        if(action.type === 'CITY_CHOSEN') {
            return action.payload;
        }
        return chosenCity
    }
    const chosenCityWeatherReducer = (weather=null, action) => {
        if(action.type === 'WEATHER_FETCHED') {
            return action.payload;
        }
        return weather
    }
    let store;
    let component;

    beforeEach(() => {
        store = mockStore({
           chosenCity: chosenCityReducer,
           weatherForecast: chosenCityWeatherReducer
        });
    });

    let div = document.createElement('div')
    component = ReactDOM.render(
        <Provider store={store}>
            <InputField />
        </Provider>
    ,div);

    it('should render with given state from Redux store', () => {
        expect(component.toJSON()).toMatchSnapshot();
    });

Is there something wrong with the mock definition? Thank you!

liooshine
  • 105
  • 6

1 Answers1

3

You're creating your component (<InputField/> wrapped in <Provider />) before the beforeEach hook has been called so mockStore hasn't been called yet so store will be undefined.

Try this:

let component;

beforeEach(() => {
  let div = document.createElement('div');
  const store = mockStore({
    chosenCity: chosenCityReducer,
    weatherForecast: chosenCityWeatherReducer
  });
  component = ReactDOM.render(
    <Provider store={store}>
      <InputField />
    </Provider>
  , div);
});

it('should render with given state from Redux store', () => {
  expect(component.toJSON()).toMatchSnapshot();
});

You can always move the store creation out of the beforeEach, if you like.

I usually have a function called renderSubject (which returns the rendered component) which I call within each test rather than using beforeEach. It reduces unnecessary mutable variables such as component being used between tests.

ourmaninamsterdam
  • 1,915
  • 15
  • 11
  • Thank you so much! It really helped a lot. Do you use getByTestId when you're testing elements? I try to use screen.getByTestId("email-input") and get "Unable to find an element by: [data-testid="email-input"]" even when I put the correct id – liooshine Jul 27 '21 at 17:25
  • 1
    Glad it could help. In regards to test ids, I refrain from using them as much as possible. They are there as an escape hatch in the case you cannot find an appropriate query. However, as Testing Library promotes accessibility by design, to find your "email-input" element you would use `screen.getByLabelText(/email/i)`, where `email` is the text used in the ` – ourmaninamsterdam Jul 28 '21 at 06:10