1

I'm using jest and enzyme to unit test my React application and I'm struggling with testing connected components.

I do have a simple component which the following logic:

class LoginPage extends React.Component {

    componentDidMount() {
        if (!this.props.reduxReducer.appBootstrapped) {
                this.props.dispatch(ReduxActions.fadeOutAndRemoveSplashScreen(500));
        }
    }

    render() {
        return (
            <div data-page="login-page" >
                <div>This is the login page.</div>
            </div>
        );
    }
}

const mapStateToProps = (state) => {
    return {
        reduxReducer: state.reduxReducer
    }
};

export default connect(mapStateToProps, null)(LoginPage);

So, this is a component which displays a <div /> element containing some text, but the important part that I want to test is that when the component is mounted, an action is dispatched to hide the splash screen. I want this only to happen when the application is not bootstrapped.

I do have a simple unit test to test that the component is rendered:

describe("[LoginPage Component]", () => {
    it("Renders without a problem.", () => {
        // Act.
        const wrapper = mount(
            <LoginPage store={ reduxStore } />
        );

        // Assert.
        expect(wrapper.find("div[data-page=\"login-page\"]").length).toBe(1);
    });
});

The reduxStore property is my actual redux store, created with the following code:

const reduxStore = createStore(
    combineReducers(
        {
            reduxReducer
        }
    )
);

Now, how can I test the componentDidMount() method, and more in special, test that the redux action fadeOutAndRemoveSplashScreen() is only called when the application is not bootstrapped yet.

I do think that I need to mock my redux store, however, I'm a newbie on this and don't now how to get started, so an example will be highly appreciated.

If any other thoughts on my implementation, feel free to provide some advice.

Kind regards

Complexity
  • 5,682
  • 6
  • 41
  • 84

1 Answers1

4

I wouldn't use the raw dispatch method to send off an action. I would use mapDispatchToProps. This makes your action directly available in your component props - here we use ES6 destructing as a short hand in the connect method.

Then instead of mocking the redux store I would just test your component without it. Try adding an export to your class (first line). For example:

export class LoginPage extends React.Component {

    componentDidMount() {
        if (!this.props.reduxReducer.appBootstrapped) {
          // make sure that you are using this.props.action() not
          // just the action(), which is not connected to redux
          this.props.fadeOutAndRemoveSplashScreen(500);
        }
    }

    render() {
        return (
            <div data-page="login-page" >
                <div>This is the login page.</div>
            </div>
        );
    }
}

const mapStateToProps = (state) => {
    return {
        reduxReducer: state.reduxReducer
    }
};

export default connect(mapStateToProps, {
  fadeOutAndRemoveSplashScreen: ReduxActions.fadeOutAndRemoveSplashScreen
})(LoginPage);

Then in your test instead of importing the connected component, import the class:

import ConnectedLoginPage, { LoginPage } from '/path/to/component';

Then simply pass the LoginPage whatever props you want to test with. So we will set your appBooststrapped to false, and then pass the action as a sinon spy:

const spy = sinon.spy();
const reduxReducer = {
  appBootstrapped: false, // or true
}
const wrapper = mount(
    <LoginPage reduxReducer={reduxReducer} fadeOutAndRemoveSplashScreen={spy} />
);

// test that the spy was called
expect(spy.callCount).to.equal(1);

This makes the test much simpler, and more importantly you are testing the component behavior - not Redux.

atwright147
  • 3,694
  • 4
  • 31
  • 57
erik-sn
  • 2,590
  • 1
  • 20
  • 37