Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ReactJS: Testing components containing components

I use Jest to test my React Components. However, I have no idea (or haven't seen anything) how to test components that pass (as prop) on methods to sub components. For instance, I have: Form, MemberList, Member, FormButton. Something similar to this in code:

Form:

<MemberList members={this.state.members} remove={this.remove} add={this.add} />
<FormButton data={this.state.members} />

MemberList:

<span onClick={this.add}> <!-- add button --> </span>
{this.props.members.map(function(member, index) {
  <Member key={index} data={member} remove={this.props.remove} />
})}

Member:

// some input like name and so, and a remove itself button.

FormButton:

var submit = function() {
    this.setState({ loading: true });

     // xhr
}
<button type="button" onClick={submit} disabled={this.state.loading}>Submit</button>

Am I thinking in the right mindset? To add, are there any practical examples out there?

*I've never tested before trying out React and Jest.

like image 419
srph Avatar asked Dec 07 '14 19:12

srph


1 Answers

The solution is to pass a mocked function directly to the sub components and test them. Anything involving more than one "sub-component" is usually not truly a unit test as you are testing multiple units of functionality.

So I would create MemberList-test.js:

describe('MemberList', function () {
  it('calls the add handler when add is clicked', function () {
    var Component = TestUtils.renderIntoDocument(
      <MemberList add={ jest.genMockFn() } />
      );

    const btn = TestUtils.findRenderedDOMComponentWithTag(Component, 'span')

    TestUtils.Simulate.change(btn);

    expect(Component.add.mock.calls.length).toBe(1)

  })
})

Then rather than trying to test your member component directly within the same test your should create Member-test.js:

describe('Member', function () {
  it('calls the add handler when add is clicked', function () {
    var Component = TestUtils.renderIntoDocument(
      <Member remove={ jest.genMockFn() } />
      );

    const btn = TestUtils.findRenderedDOMComponentWithTag(Component,
      'HOWEVER YOU FIND YOUR REMOVE BUTTON')

    TestUtils.Simulate.change(btn);

    expect(Component.remove.mock.calls.length).toBe(1)

  })
})

Now the assertion your are missing is that the remove handler that is passed into the member list is correctly getting passed down to the Member component. So let's add another test to the MemberList-test.js

it('passes correct information to the children', function () {
  var MemberMock = require('../Member')
  var removeFn = jest.genMockFn();

  var testMember = {WHATEVER YOUR MEMBER OBJECT LOOKS LIKE}

  var Component = TestUtils.renderIntoDocument(
    <MemberList members={ [testMember] }
      remove={ removeFn } />
    );

  // We expect the member component to be instantiated 1 time and
  // passed the remove function we defined
  // as well as a key and the data
  expect(MemberMock.mock.calls).toEqual([[{key: 0, data: testMember,
    remove: removeFn}]])

})

Then you simply do the same pattern with the form component. Mocking the member list and the button and testing them separately and seeing that the correct handlers and data are passed down.

Hopefully this makes sense and if not just reply and maybe I can walk you through it on Skype or something.

like image 143
SegFaultx64 Avatar answered Sep 24 '22 04:09

SegFaultx64