Test a form with Jest and React JS TestUtils
Asked Answered
A

1

12

I have a form with 3 radio buttons like follows (fake names):

<form className="myForm" onSubmit={this.done}>
  <input className="myRadio" checked={ŧrue} type="radio" name="myRadio" onChange={this.change} value="value1"
  <input className="myRadio" type="radio" name="myRadio" onChange={this.change} value="value2"
  <input className="myRadio" type="radio" name="myRadio" onChange={this.change} value="value3"
<input type="submit" className="submit" />
</form>

And I am having very hard time trying to test the onChange and the onSubmit events.

inputs = TestUtils.scryRenderedDOMComponentsWithClass(MyComponentRendered, 'myRadio');
myForm = TestUtils.findRenderedDOMComponentWithClass(MyComponentRendered, 'myForm');

I have a test like:

it("changes the checked state when clicked", function() {
  MyComponent.change = jest.genMockFunction();

  expect(inputs[0].getDOMNode().checked).toBe(true);
  TestUtils.Simulate.change(inputs[1], {target: {value: 'value2'}});
  expect(inputs[0].getDOMNode().checked).toBe(false);
  expect(inputs[1].getDOMNode().checked).toBe(true);
  expect(inputs[2].getDOMNode().checked).toBe(false);

  expect(MyComponent.change).toBeCalled(); //Fails
  expect(MyComponent.change.mock.calls.length).toBe(1); //Fails too
});

That works except for the function (MyComponent.change) that should be called but it is not.

I also have one test for onSubmit:

it("saves on submit", function()
  MyComponent.done = jest.genMockFunction();
  MyComponent.insideDone = jest.genMockFunction();
  TestUtils.Simulate.submit(myForm);
  expect(MyComponent.done).toBeCalled(); //Fails
  expect(MyComponent.insideDone).toBeCalled(); //Success
});

Notice: MyComponent.insideDone is a function that is called by 'done' function.

Which fails too. I am pretty sure that the problem here is that I am not simulating the events in a correct way. However, I didn't find example of this using Jest and TestUtils from React.

Attic answered 20/10, 2014 at 16:41 Comment(8)
I tried submitting a form using the click event but ended up nowhere, but what helped was getting hold of the form element itself and calling TestUtils.Simulate.submit(form)Nucleoside
As for the change function I would probably check MyComponent.change.mock.calls.length to be equal to 1, to check that the mocked function has been called.Nucleoside
@nimgrg, that mock.calls.length fails too. I updated my answer. Looks like for the last example, "done" function calls another function... and that one it's reported as called.Attic
Not really sure I have limited and painful experience with Jest and React tests myself. What do you get when you do 'console.log(MyComponent.change.mock.calls)'Nucleoside
Oh, that just prints "[]".Attic
Ignore my earlier comment. I tried the same setup as to yours and I see the same problem. The done function that is being called when the form is submitted is the actual done function with which the component was rendered, the mocked done function never gets called. Hopefully someone can shed some light on it.Nucleoside
@FerranNegre can you show some more source from your component? A jsbin/fiddle would also be of great help.Squaw
I opened an issue here github.com/facebook/jest/issues/232 . @NickTomlin you can just write a component like the one from the issue, it is stateless and with a checkbox in the render function.Attic
N
2

The problem is that you are replacing the function after you have already given the original function to React. The expression onSubmit={this.done} is that function, and that is set as the event handler. After the render function finishes, you replace instance.done but React already got the old function. What you should do is instead:

<form className="myForm" onSubmit={() => this.done()}>

This makes sure that the event handler always invokes the method on the instance (the one you replaced). This has the nice side effect of being future compatible with React, since they will stop autobinding all methods to the instance.

Nitz answered 16/4, 2015 at 11:30 Comment(5)
Excellent answer! The only change I had to make was to pass the event to the method invocation and then to add the event.target.name to the event I was generating to get my handler to work right. The jsx code: onChange={(e) => this._handleChange(e)}Amigo
Great! Would you mind marking it as the correct answer?Nitz
would but it's not my question ;) but I did up-vote!Amigo
Haha, sorry, I assumed you were the OP since you commented.Nitz
Yay that worked. Just for the record, using still ES5 I needed to do: onSubmit={function(){return this.done()}.bind(this)}Attic

© 2022 - 2024 — McMap. All rights reserved.