Unit test form submission with data using react testing library
Asked Answered
D

2

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.

Decagram answered 1/5, 2020 at 5:29 Comment(0)
C
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

Caricature answered 1/5, 2020 at 6:14 Comment(2)
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?Dobbin
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.Sarsenet
S
1

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.
Sarsenet answered 4/9, 2023 at 5:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.