0

I have the following connected component in React-Redux

export class IncrementalSearch extends React.Component {

    constructor(props) {
        super(props);
        this.onSearch$ = new Subject();
        this.onChange = this.onChange.bind(this);
    }

    componentDidMount() {
        this.subscription = this.onSearch$
            .debounceTime(300)
            .subscribe(debounced => {
                this.props.onPerformIncrementalSearch(debounced);
            });
    }

    componentWillUnmount() {
        if (this.subscription) {
            this.subscription.unsubscribe();
        }
    }

    onChange(e) {
        const newText = e.target.value;
        this.onSearch$.next(newText);
    }

    render() {
        return (
            <div className={styles.srchBoxContaner}>
                <input
                    className={styles.incSrchTextBox}
                    type="text" name="search" id="searchInput" placeholder="Search.."
                    onChange={this.onChange}
                />
            </div>
        );
    }
}

const mapDispatchToProps = (dispatch) => ({
    onPerformIncrementalSearch: (searchText) => {
        dispatch(performIncrementalStoreSearch(searchText));
    }
});

const IncrementalSearchComponent = connect(null, mapDispatchToProps)(IncrementalSearch);
export default IncrementalSearchComponent;

I'm now trying to write a unit tests for the connected component. I'm using Jest, Enzyme, and Sinon. So far this is what my unit test looks like

it('calls \'onPerformIncrementalSearch\' when the user types in something', () => {
    const mockStore = configureStore();

    const onPerformIncrementalSearchSpy = sinon.spy();
    const mapStateToProps = null;
    const mapDispatchToProps = {
        onPerformIncrementalSearch: onPerformIncrementalSearchSpy
    };

    const mappedProps = { mapStateToProps, mapDispatchToProps };

    const incrementalSearchWrapper =
        mount(
            <Provider store={mockStore}>
                <IncrementalSearchComponent
                    onPerformIncrementalSearch={onPerformIncrementalSearchSpy}
                    props={mappedProps}
                    store={mockStore}
                />
            </Provider>
        );


    //find the input element
    const searchInput = incrementalSearchWrapper.find('#searchInput');
    searchInput.node.value = 'David';
    searchInput.simulate('change', searchInput);
    expect(onPerformIncrementalSearchSpy.called).toEqual(true);
    // onChangeSpy.restore();
});

However, when I run this test, I get the following error

TypeError: Cannot read property 'bind' of undefined

How do I fix this?

Pang
  • 9,564
  • 146
  • 81
  • 122
tmp dev
  • 8,043
  • 16
  • 53
  • 108

1 Answers1

4

Testing connected components can be a huge pain. I find that it's more trouble than it's worth to try to wrap your components with a Provider to give them access to the store.

Instead, I would just export the component, mapStateToProps, and mapDispatchToProps and test them individually. Your app will still work the same if you export the connected component as the default.

Dan Abramov (Co author of Redux) suggests this approach in this comment

I would also suggest looking into enzyme shallow rendering instead of using mount when testing connected components.

bill
  • 1,646
  • 1
  • 18
  • 27
  • But the connected component is more than just mapStateToprops and mapDispatchToProps, there is other logic in the component which I need to test. Tehrefore how do I test this? – tmp dev Aug 10 '17 at 22:51
  • You can still test the logic in the component through this approach. You would just have two `exports`. One would be to export the connected component `export default connect ...` and you would import this in your app through `import { default as myComponent }`. You would also export the unconnected component like `export class myComponent` and import in your test like `import { myComponent }`. Then you can test all of the logic in the component without having to worry about accessing the store and testing the connected component. – bill Aug 10 '17 at 22:59
  • [This answer](https://stackoverflow.com/a/35578985/6326906) might help to explain further. – bill Aug 10 '17 at 23:01
  • maybe I am not understanding something, I have exported both connected and unconnected component, the issue comes when I just try to mount or shallow the connected component just to test its logic. Is there a working example that I could use? – tmp dev Aug 11 '17 at 00:15
  • You will still have the same issue if you try to mount (or shallow mount) the connected component. Exporting the connected component is strictly for accessing it in your app, not in your tests. I would just import the unconnected component and test that. You should still be able to test all of the logic in the component, minus that `connect` works. If there is some other logic that you must test through mounting the connected component, then this approach is not right for you. – bill Aug 11 '17 at 15:20
  • Yes this is my whole concern, I have some logic in the connected component which I want to test, and for some reason I cannot get it to mount. – tmp dev Aug 12 '17 at 00:21
  • 1
    bill's point is that you don't need to test the `connect` function since that's not your code. Your code is all in the unconnected component and `mapState/DispatchToProps`. If you test those, then you've tested all of your logic. – stone Dec 11 '17 at 23:37