35

I have a React component which is enclosed within Higher Order Component withRouter as below:

module.exports = withRouter(ManageProfilePage);

My routes are as below:

<Route path="/" component={AdrApp}>
    <IndexRoute component={Login}/>
    <Route component={CheckLoginStatus}>
        <Route path="manage-profiles/:profileId" component=
        {ManageProfilesPage}/>
     </Route>
    <Route path="*" component={notFoundPage}/>
</Route>

I need to use once of the Router lifecycle methods, that is why I need withRouter:

class ManageProfilePage extends React.Component {
    componentDidMount() {
    this.props.router.setRouteLeaveHook(this.props.route, () => {
      ...
    })
    render(){
    ... 
    }
}

I need to test this component using Jest/Enzyme and I wrote the test case as below:

describe('manage profile page test suite', () => {


    it('snapshot test', () => {

        const setRouteLeaveHook =jest.fn();

        let wrapper = shallow(
            <ManageProfilePage params={{id : 25, router: 
        setRouteLeaveHook}}/>
        );
      expect(wrapper).toMatchSnapshot();
    })
   }) 

The issue is it is not rendering one level deep. I am pasting the snapshot below:

exports[`manage drug term page test suites snapshot test 1`] = `
<ManageProfilePage
  params={
    Object {
      "id": 25,
      "router": [Function],
    }
  }
/>
`;

Is there any different way I can write my test case so that I am able to render ManageProfilePage atleast 1 level deep? It is not able to render as it is enclosed within WithRouter? How do we test these type of components?

Canta
  • 1,480
  • 1
  • 13
  • 26
Amol Aggarwal
  • 2,674
  • 3
  • 24
  • 32

4 Answers4

55

Normally if we try to test such components we won’t be able to render it as it is wrapped within WithRouter (WithRouter is a wrapper over a component which provides Router props like match, route and history to be directly used within the component). module.exports = withRouter(ManageProfilePage);

To render such components, we have to explicitly tell it to render the wrapped component using WrappedComponent keyword. For Eg. we will use below code for snapshot test:

describe('manage profile page test suite', () => {


    it('snapshot test', () => {

        const setRouteLeaveHook =jest.fn();

        let wrapper = shallow(
            <ManageProfilePage.WrappedComponent params={{id : 25, router: 
        setRouteLeaveHook}}/>
        );
      expect(wrapper).toMatchSnapshot();
    })
   }) 

This will tell enzyme to do shallow rendering (Shallow Rendering renders only that particular component and skips child components) for ManageProfilePage which is wrapped component within WithRouter.

ZEE
  • 5,669
  • 4
  • 35
  • 53
Amol Aggarwal
  • 2,674
  • 3
  • 24
  • 32
4

Shallow rendering will only render one level, that's part of the specs for it.

you can use Mount which will render the whole tree, but I don't think you can limit how many levels deep it will render.

In any case, when using High Order Components I usually just export the base component as well(before wrapping it), this way I can do all my tests without the wrapper, and simply pass mocks for the required providers.

same thing with a Connect component with redux, you export your regular component and test the different props on that, instead of the connected one.

also note that some with... wrappers do not expose the inner instance(some do, but some don't) , so testing on your own component instead of the wrapper helps there as well.

Patrick
  • 3,289
  • 2
  • 18
  • 31
  • the problem is shallow rendering is not rendering not even 1 level deep in my case – Amol Aggarwal May 26 '17 at 17:15
  • I think it is, you don't know what's going on exactly inside the withRouter - something `IS` being rendered, it's just not what you expect. I still suggest testing your own component and not wrappers – Patrick May 27 '17 at 07:50
  • I mean it is not rendering at component level -of course it will render at withRouter level. – Amol Aggarwal Jun 09 '17 at 18:01
  • Could you provide an example how you do this Patrick? I'm thinking of doing the same but can't figure out the approach exactly – Chris Nov 19 '17 at 22:21
  • Hey chris, the best approach is to export your regular component and just run tests on that instead, you're not after testing the react router after all – Patrick Nov 19 '17 at 22:25
  • @Patrick could you please look at https://stackoverflow.com/questions/53845187/writing-the-unit-test-case-for-the-container-in-the-react-js?noredirect=1#comment94538635_53845187 – ganesh kaspate Dec 19 '18 at 06:27
  • 8
    Having to export something solely for the purpose of testing seems like an anti-pattern – Jens Bodal Apr 06 '19 at 06:40
  • @JensBodal - While I generally agree (writing code for the sole purpose of testing is bad), the alternative it set up quite a bit of boiler plate to support testing a redux connected component. – Patrick Apr 06 '19 at 17:39
  • @Patrick one problem I have found in testing redux react connected components by directly exporting the unconnected component is that your mapstatetoprops and mapdispatchtoprops functions are not tested that way. you may have to write seperate tests for them – sktguha Jul 15 '20 at 20:21
  • 1
    @SaikatGuha - True, but those are very simple test. The major downside is that you're not testing everything together. – Patrick Jul 16 '20 at 16:42
  • @Patrick Yes agreed. The best way is to test the connected components even though that may involve a bit more boilerplate – sktguha Jul 16 '20 at 16:56
  • @sktguha I'd argue against things like "Best" - For my team (3 years ago) it was much better to test things in separation, as we had a manual QA process as well. Sometimes the unit tests are really complex to test something really simple, that's never a good thing – Patrick Jul 16 '20 at 17:37
2

I think you should try this

    describe('manage profile page test suite', () => {


    it('snapshot test', () => {
        let wrapper = shallow(
            <ManageProfilePage.WrappedComponent/>
        );
      expect(wrapper).toMatchSnapshot();
    })
   }) 
Simas Joneliunas
  • 2,890
  • 20
  • 28
  • 35
-8

What I did which fixed the problem:

In "this.props.match.params.id" is used the post component

In the test case

const miniProps = {
    otherProps,
    match:{params:{id:'112'}}
};

const wrapper = shallow();  

Frank
  • 1
  • 1