Jonathan
Jonathan

Reputation: 32868

In a React/Jest unit test, how do you simulate an event prop being called by a mocked component?

Suppose I have a React component that is a Login form with a Username field. If the user sets the Username to a blank string, the component shows an error message.

Here's the code:

const LoginForm = () => {
  const [errorMessage, setErrorMessage] = useState(''),
    handleOnChangeUsername = (newUsername: string) => {
      if (newUsername.trim() === '') {
        setErrorMessage('Username is required.');
      } else {
        setErrorMessage('');
      }
    };

  return (
    <form>
      <UsernameField onChange={handleOnChangeUsername} />
      {errorMessage && <Error message={errorMessage} />}
    </form>
  );
};

I want to unit test this component.

So I want to write a test that verifies that when the user enters a blank username, then the error message: 'Username is required' is displayed underneath.

In order to write this unit-test, I assume that I need to do the following:

  1. Mount the LoginForm component, providing a mocked UsernameField component.

  2. Get the mocked UsernameField component to call its onChange prop, passing in a blank string.

  3. Assert that the LoginForm component is now rendering an Error component (i.e. that the code in handleOnChangeUsername actually ran and produced the correct side-effect).

The mocking part is easy – I just call jest.mock(...), passing in the path to the module and a mock implementation.

The tricky bit is Step 2. How do I make my unit test cause the mock to call one of its own props, to simulate the onChange being called with an empty string?

It seems like there would have to be some channel by which the unit test code could communicate with the internal props of the mocked component.

Is there a simple and/or idiomatic way to do this in a React/Jest unit test?

Note: This question assumes a mockist style. I know that some developers might say that I should simply trigger the Username onChange directly, by manipulating the DOM element that the Username component renders. I already know how to do that. But I want to follow a mockist style and completely mock the Username component.

Upvotes: 1

Views: 1850

Answers (1)

Jonathan
Jonathan

Reputation: 32868

Ok, I seem to have found at least one way of accessing the props. It requires Enzyme, though; still not sure how to do this without Enzyme.

Enzyme's wrapper includes a props() method, which returns the props of the wrapped component.

So I can write this:

const root = mount(<LoginForm />);
root.find("UsernameField").props().onChange("");
expect(root.find("Error").exists()).toBeTruthy();

Upvotes: 1

Related Questions