Pass jest.fn() function to mapDispatchToProps in enzyme shallow render
Asked Answered
C

3

6

Having very simple component:

import PropTypes from 'prop-types' import React from 'react' import { connect } from 'react-redux'

class MyComponent extends React.Component {
  componentWillMount() {
    if (this.props.shouldDoSth) {
      this.props.doSth()
    }
  }

  render () {
    return null
  }
}

MyComponent.propTypes = {
  doSth: PropTypes.func.isRequired,
  shouldDoSth: PropTypes.bool.isRequired
}

const mapStateToProps = (state) => {
  return {
    shouldDoSth: state.shouldDoSth,
  }
}

const mapDispatchToProps = (dispatch) => ({
  doSth: () => console.log('you should not see me')
})

export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)

I want to test if doSth is called when shouldDoSth is equal true.

I've written a test:

describe('call doSth when shouldDoSth', () => {
  it('calls doSth', () => {
    const doSthMock = jest.fn()
    const store = mockStore({shouldDoSth: true})
    shallow(<MyComponent doSth={doSthMock}/>, { context: { store } }).dive()
    expect(doSthMock).toHaveBeenCalled()
  })
})

but it seems that although I pass doSth as props it gets overridden by mapDispatchToProps as console.log('im not a mock') is executed.

How to properly pass/override/assign doSth function to make component use mock instead of function from mapDispatchToProps. Or maybe I'm doing something which should not be allowed at all and there is 'proper' way of testing my case. Shall I just mock dispatch instead and check if it is called with proper arguments?

Chalkstone answered 22/6, 2018 at 14:11 Comment(0)
M
3

I think one thing you need to figure out is whether you want doSth to be a prop, or a redux action connected in mapDispatchToProps.

If it's a prop, then you would connect it to redux in a parent (container). Remove it from this component's mapDispatchToProps. This would make the component more testable.

If you want it to be a redux action connected in this component, then it would make sense to move the action out of this component, somewhere like actions.js, import it in this component, and then mock it in the test jest.mock('actions.js', () => ({doSth: jest.mock()}))

Marpet answered 22/6, 2018 at 19:37 Comment(0)
H
2

Export the unconnected component and use it in the test and you will be able to override the mapDispatchToProps action.

export class MyComponent extends React.Component {
  componentWillMount() {
    if (this.props.shouldDoSth) {
      this.props.doSth()
    }
  }

  render () {
    return null
  }
}

MyComponent.propTypes = {
  doSth: PropTypes.func.isRequired,
  shouldDoSth: PropTypes.bool.isRequired
}

const mapStateToProps = (state) => {
  return {
    shouldDoSth: state.shouldDoSth,
  }
}

const mapDispatchToProps = (dispatch) => ({
  doSth: () => console.log('you should not see me')
})

export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)




import {MyComponent} from '../MyComponent'
describe('call doSth when shouldDoSth', () => {
  it('calls doSth', () => {
    const doSthMock = jest.fn()
    const store = mockStore({shouldDoSth: true})
    shallow(<MyComponent doSth={doSthMock}/>, { context: { store } }).dive()
    expect(doSthMock).toHaveBeenCalled()
  })
})
Halsted answered 24/7, 2020 at 8:10 Comment(0)
T
0

I think that you should ask yourself if you want to test the unconnected MyComponent or the connected one.

Here you have two discussions that may help you: Am I testing connected components correclty? and Can't reference containers wrapped in a Provider or by connect with Enzyme

If you are not testing the action nor state properly said, you might forget about mapStateToProps and mapDispatchToProps (those processes are already tested by redux) and pass values through props.

Check the following example:

describe('MyComponent', () => {

let wrapper;

const doSthMock = jest.fn();

beforeEach(() => {

    const componentProps = {
        doSth: true,
    };

    wrapper = mount(
            <MyComponent
                {... componentProps}
                doSth={doSthMock}
            />
    );

});

it('+++ render the component', () => {
    expect(wrapper.length).toEqual(1);
});

it('+++ call doSth when shouldDoSth', () => {
    expect(doSthMock).toHaveBeenCalled();
});

})

Tiebout answered 13/12, 2019 at 21:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.