2
votes

how can i drill down to the child component and simulate the click.

// parent component

_handleChildClick = () => {
      this.setState({
        enabled: false
      })
    }

    <div>
      <Child 
     onChildClick={this._handleChildClick}
    />
    </div>

//child component

<div>
  <button className="toggle"
    onClick={props.onChildClick}
  ></button>
</div>

//test.js

const renderedComponent = shallow(<Parent />)
console.log(renderedComponent.debug()) // i am not able to drill down
// renderedComponent.find('toggle').simulate('click') wont work !!!

if i use mount since my component some api call initially it will have loader component

even i tried with below snippet the parent is not even coming

it('test with mount', async () => {
   const a = await mount(<Parent />)
   console.log(a.debug()) // showing the loader only
})

how can i resolve this issue, Any help appreciated :)

// App.js

import React, {Component, Fragment} from 'react'
import Child from './child'

class App extends Component{

  state = {
    data: null,
    enable: false
  }

  componentDidMount(){
    this.getData()
  }

  getData = async () => {
    const response = await fetch('http://www.example.com');
    const data = await response.json();
    this.setState({
      data
    })
  }

  _handleChildClick = () => {
    this.setState({
      enable: true
    })
  }

  render(){
    const {data, enable} = this.state
    if(!data){
      return (
       <div>
         Loading
       </div>
      )
    }else{
      <Fragment>
        <Child
         handleChildClick={this._handleChildClick}
        />
      </Fragment>
    }
  }
}


export default App


import React from 'react';

const child = () => {
  return(
    <div>
      <button
        className="toggle"
        onClick={props.handleChildClick}
      >
      Toggle
      </button>
    </div>
  )
}

export default child

// App.test.js

import React from 'react';
import {enzyme} from 'enzyme';
import App from './App';

describe("App test cases", () => {
  it('should trigger _handleChildClick', async () => {
    window.fetch = jest.fn().mockImplementation(() => ({
      status: 200,
      json: () => new Promise((resolve, reject) => {
        resolve(
            {
              name: "some data"
            }
        )
      })
    })) 
    const mountWrapper = await mount(<App />)
    mountWrapper.update()
    console.log("mountWrapper", mountWrapper.debug()) // showing the loader one
    setTimeout(() => {
      console.log("mountWrapper", mountWrapper.debug()) // nothing showing
      // expect(mountWrapper.find('.toggle').length).toEqual(1)
    },0)
  })
})
1
You have to wait until the api call (or its stub) completes and the Child is rendered.Brian Adams
how can i do that one, but i am doing async and await so it should go through all the lifecycle and wai for the api and get fully rendered. What i am doing hereLearner
Can you update your question with more component code? I can't see enough of it to help right now.Brian Adams
@brian-lives-outdoors updated can you check what may be i am doing wrongLearner

1 Answers

3
votes

Shallow rendering (which you are using for your current tests) only renders one level deep. This means that you won't be able to simulate the render() method or the behaviour of child components using the shallow renderer. See the documentation here for more info.

If you want to effectively unit test this code example, you shouldn't try and simulate its child behaviour. Instead, you should test the <Parent /> and <Child /> separately. This prevents changes in your Child component affecting the test results in your Parent component, or vice versa. This is pretty much the whole reason the shallow renderer exists! From the documentation:

Shallow rendering is useful to constrain yourself to testing a component as a unit, and to ensure that your tests aren't indirectly asserting on behaviour of child components.

I would write this test for the child component, using a mock function for its onChildClick prop:

test('triggers its onChildClick prop when clicked', () => {
    const mock = jest.fn()
    const wrapper = shallow(<Child onChildClick={mock} />)
    wrapper.find('.toggle').simulate('click')
    expect(mock).toHaveBeenCalledTimes(1)
})

And I would write this test for the parent, passing in the data to ensure the child is rendered:

test('sets its state when its child is clicked', (done) => {
    const wrapper = shallow(<App data={{ test: 'test' }}/>)
    expect(wrapper.state('enable')).toBe(false)

    wrapper.find('Child').props().onChildClick()
    expect(wrapper.state('enable')).toBe(true)
})

Just tried both of these tests and they work fine. I'm testing the full functionality of the components here, except the process is separated into units. The child triggers its onChildClick prop when the button is clicked, and triggering that prop inside the parent sets the state of enable to true.

Full-DOM rendering (with mount(<Parent />))is a lot more messy, and I only really find it useful for testing components that use DOM apis (which shouldn't happen much in React anyway). Have a look at the documentation again for a fuller explanation!

If you want to test larger-scale features of your app (which it looks like you do), I'd suggest using an end-to-end testing framework like Cypress.

Also, one last thing... you're using wrapper.find('toggle'), but if you want to find a node by its className, then you need to do wrapper.find('.toggle') instead.

Hope this helps!