1

Full Repo

https://github.com/brianbancroft/sample-testing-msw-sb-jest-rtl

Using mock service worker (msw) to respond to a network request, I have a React login component. On submit it sends a network request and on success it is supposed to trigger a "handleClose" function which is a prop to the component.

async function handleSubmit(event) {
    event.preventDefault();
    setLoading(true);

    const { username, password } = event.target.elements;

    try {
      const response = await fetch("/login", {
        method: "POST",
        mode: "cors",
        cache: "no-cache",
        credentials: "same-origin",
        headers: {
          "Content-Type": "application/json",
        },
        redirect: "follow",
        referrerPolicy: "no-referrer",
        body: JSON.stringify({
          username: username.value,
          password: password.value,
        }),
      });
      await response.json();

      if (response.status === 200) {
        props.handleClose();
      } else {
        setInvalidUsernamePassword(true);
        setLoading(false);
      }
    } catch (error) {
      alert("Error detected", error);
    }
  }

When I test, I validate that the function handleClose is invoked.


const setup = async () => {
  const handleClose = jest.fn();
  const utils = await render(<Primary handleClose={handleClose} />);
  const usernameField = screen.getByLabelText(/Username/);
  const passwordField = screen.getByLabelText(/Password/);
  const submitButton = screen.getByRole("button", {
    name: /Sign In/i,
  });

  return {
    usernameField,
    passwordField,
    submitButton,
    handleClose,
    ...utils,
  };
};

test("The handleClose function prop is triggered", async () => {
  const { usernameField, passwordField, submitButton, handleClose } =
    await setup();
  fireEvent.change(usernameField, { target: { value: faker.lorem.word() } });
  fireEvent.change(passwordField, { target: { value: faker.lorem.word() } });

  fireEvent.click(submitButton);

  expect(handleClose).toHaveBeenCalled();
});

This fails


    Expected number of calls: >= 1
    Received number of calls:    0

However, when I invoke the prop function before the start of the async function, it passes, which tells me it's not how I am passing the jest.fn() into the component.

I have investigated whether an error gets thrown (it doesn't), and I also know that msw is working as intended, and that the network request responds with a 200 unless I send a password value of "badpassword" which is used to simulate an incorrect email or password in another test in the same file.

I also converted the function to use promises. No change.

brianbancroft
  • 463
  • 3
  • 17

1 Answers1

0

This is what the waitFor utility method is meant for in React testing library.

https://testing-library.com/docs/dom-testing-library/api-async/


  test("The handleClose function prop is triggered", async () => {
    const { usernameField, passwordField, submitButton, handleClose } =
      await setup();

    fireEvent.change(usernameField, { target: { value: faker.lorem.word() } });
    fireEvent.change(passwordField, { target: { value: faker.lorem.word() } });

    await fireEvent.click(submitButton);

    await waitFor(() => expect(handleClose).toHaveBeenCalled());
  });

The test passes now.

brianbancroft
  • 463
  • 3
  • 17