14

I have a react component with a form. I want to unit test (using jest and RTL) if form gets submitted with correct data. Here are my component and unit test method:

Component:

class AddDeviceModal extends Component {
  handleOnSave(event) {
    const { deviceName } = event.target;
    const formData = {
      deviceName: deviceName.value,
    };
    this.props.onSave(formData);
  }

  render() {
    return (
      <Form onSubmit={this.handleOnSave}>
        <Form.Label>Device Name</Form.Label>
        <Form.Control name="deviceName" placeholder="Device Name" required />
        <Button type="submit">Save Device</Button>
      </Form>
    );
  }
}

Unit Test:

it("Test form submit and validation", () => {
  const handleSave = jest.fn();
  const props = {
    onSave: handleSave,
  };
  render(<AddDeviceModal {...props} />);
  const deviceNameInput = screen.getByPlaceholderText(/device name/i);
  fireEvent.change(deviceNameInput, { target: { value: "AP VII C2230" } });
  fireEvent.click(getByText(/save device/i));
});

However, in handleOnSave(), I get error as deviceName is undefined. For some reason, it is not able to get the textbox value from event.target. Am I doing something wrong in above code? Needed help in fixing this issue.

2 Answers 2

14

The problem you have it with trying to access the input directly from event.target. You should access it from event.target.elements instead: https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/elements.

function handleOnSave(event) {
  event.preventDefault();
  const { deviceName } = event.target.elements;
  const formData = {
    deviceName: deviceName.value
  };

  // this will log the correct formData even in tests now
  console.log(formData);
  this.props.onSave(formData);
}

And here is your test:

it("Test form submit and validation", () => {
  const { getByPlaceholderText, getByText } = render(<App />);
  const deviceNameInput = getByPlaceholderText(/device name/i);

  fireEvent.change(deviceNameInput, { target: { value: "AP VII C2230" } });
  fireEvent.click(getByText(/Save Device/i));
});

I created a codesandbox where you can see this in action: https://codesandbox.io/s/form-submit-react-testing-library-45pt8?file=/src/App.js

Sign up to request clarification or add additional context in comments.

2 Comments

GTK. What's the difference though when running test? Why we can't just use the . with the input id as we would in the browser?
How is this test adding any value without any expect statement? It's just simply rendering the App component and getting the button element and firing a click event. It is not testing if the onSubmit is called or not.
2

I used jest and react-testing-library.

I personally don't like the idea of changing the Form component code by passing a mock or spy function just for testing purpose.

For my form, I come up with this idea (find the code samples and full answer here) -

  • basically I created handleOnSubmitMock function, and then assigned it to screen.getByRole("form", { name: "signup-form" }).onsubmit GlobalEventHandler. (Not passing the mock function to the Form)
  • then I checked if expect(handleOnSubmitMock).toHaveBeenCalled() or expect(handleOnSubmitMock).not.toHaveBeenCalled() passes.

You should be able to modify the code for your need from the descriptive code samples in the link, and it should work. The goal is to -

  • render the Form component.
  • get the necessary elements.
  • fire the button click event.
  • expect onSubmit handler to be either called or not called depending on the test conditions.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.