最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Jest mock for react-select or react-testing-library on change not firing on target change - Stack Overflow

programmeradmin0浏览0评论

I'm currenty trying to mock the react-select ponent and no matter what i do the firevent.change from react-testing-library seems to only fire on the first call of firevent.change.

I was wondering why the change was only firing once and how to fix it.

My jest mock is as follows:

jest.mock("react-select", () => ({ options, value, onChange }) => {
  function handleChange(event) {
    console.log("called");

    const option = options.find(option => {
      return option.value == event.currentTarget.value;
    });

    onChange(option);
  }
  return (
    <select
      id="uc"
      data-testid="select"
      value={value}
      onChange={event => handleChange(event)}
    >
      {options?.map(({ label, value }) => (
        <option key={value} value={value}>
          {label}
        </option>
      ))}
    </select>
  );
});

As you can see its a fairly simple select block and i thought i should be able to call.

fireEvent.change(selectOptions[0], { target: { value: "0" } });

And this will work however if i then chain them together as in the following example:

test("Should render add another button and function", () => {
  const mockSetOpen = jest.fn();

  const { queryAllByTestId, getByTestId, getByLabelText, debug } = renderWithRedux(
    <TabByRestraints setOpen={mockSetOpen} hidden={false} />,
    {
      userCatagories: userCategoriesMock,
      permissionGroups: permissionGroupsMock,
      roles: rolesMock
    }
  );

  // Check that the user catagories exist and user catagories values
  const selectOptions = queryAllByTestId("select");
  expect(selectOptions).toHaveLength(2);

  expect(getByTestId("user-category-row")).toBeTruthy();

  fireEvent.change(selectOptions[0], { target: { value: "0" } });
  fireEvent.change(selectOptions[1], { target: { value: "132" } });

  expect(getByLabelText("Add a new user category restraint")).toBeTruthy();
});

I can see that the console.log("called") is only fired once. The second select on the page is never has its value selected therfore the test fails.

I would like both change to dispatch a change event.

fireEvent.change(selectOptions[0], { target: { value: "0" } });
fireEvent.change(selectOptions[1], { target: { value: "132" } }); 

However it only ever sets of the first.

Any help greatly appeciated.

I'm currenty trying to mock the react-select ponent and no matter what i do the firevent.change from react-testing-library seems to only fire on the first call of firevent.change.

I was wondering why the change was only firing once and how to fix it.

My jest mock is as follows:

jest.mock("react-select", () => ({ options, value, onChange }) => {
  function handleChange(event) {
    console.log("called");

    const option = options.find(option => {
      return option.value == event.currentTarget.value;
    });

    onChange(option);
  }
  return (
    <select
      id="uc"
      data-testid="select"
      value={value}
      onChange={event => handleChange(event)}
    >
      {options?.map(({ label, value }) => (
        <option key={value} value={value}>
          {label}
        </option>
      ))}
    </select>
  );
});

As you can see its a fairly simple select block and i thought i should be able to call.

fireEvent.change(selectOptions[0], { target: { value: "0" } });

And this will work however if i then chain them together as in the following example:

test("Should render add another button and function", () => {
  const mockSetOpen = jest.fn();

  const { queryAllByTestId, getByTestId, getByLabelText, debug } = renderWithRedux(
    <TabByRestraints setOpen={mockSetOpen} hidden={false} />,
    {
      userCatagories: userCategoriesMock,
      permissionGroups: permissionGroupsMock,
      roles: rolesMock
    }
  );

  // Check that the user catagories exist and user catagories values
  const selectOptions = queryAllByTestId("select");
  expect(selectOptions).toHaveLength(2);

  expect(getByTestId("user-category-row")).toBeTruthy();

  fireEvent.change(selectOptions[0], { target: { value: "0" } });
  fireEvent.change(selectOptions[1], { target: { value: "132" } });

  expect(getByLabelText("Add a new user category restraint")).toBeTruthy();
});

I can see that the console.log("called") is only fired once. The second select on the page is never has its value selected therfore the test fails.

I would like both change to dispatch a change event.

fireEvent.change(selectOptions[0], { target: { value: "0" } });
fireEvent.change(selectOptions[1], { target: { value: "132" } }); 

However it only ever sets of the first.

Any help greatly appeciated.

Share Improve this question edited Feb 18, 2020 at 11:51 skyboyer 23.8k7 gold badges62 silver badges71 bronze badges asked Feb 18, 2020 at 10:40 MorphasisMorphasis 1,4331 gold badge13 silver badges27 bronze badges 6
  • Are you sure there is an option with value 132 for the second select rendered at the time when fireEvent.change executes? – Christos Lytras Commented Feb 21, 2020 at 15:26
  • Yes because i used debug() and i can see the options rendering correcty with there values. I also tested using the same select again (selectOptions[0]) but instead kust changing its value again same issue persists sadly. – Morphasis Commented Feb 21, 2020 at 15:45
  • I am very curious to why this isn't working, so I created a simple React app using react-select to render 2 select inputs with such values. It's working fine for me. Here is the screenshot. Of course I haven't implemented any state management with redux which may be the culprit, but I am not sure. The only difference is that instead of renderWithRedux I'm using render from @testing-library/react. I can upload that to a repository if you're interesting. – Christos Lytras Commented Feb 21, 2020 at 17:03
  • Please do its strange that the state change would cause this. It can probbably be resovled then with some kind of timeout or promise structure. – Morphasis Commented Feb 21, 2020 at 17:09
  • Here you can find the repo. If it doesn't run the tests when you run yarn test then just hit a to run all tests. Let me know if you have any trouble running the test and see the results. – Christos Lytras Commented Feb 21, 2020 at 17:24
 |  Show 1 more ment

2 Answers 2

Reset to default 4 +25

You should do another query for the select elements. I would go further and name them differently to make the test easier to read/debug.

  // it's easier to have separate ids for each select, will be easier to read/maintain the test in the future
  const firstSelect = queryByTestId("select1");
  fireEvent.change(firstSelect, { target: { value: "0" } });

  // a rerender might have happened so you need to query for the second select after the first event.
  const secondSelect = queryByTestId("select2");
  fireEvent.change(secondSelect, { target: { value: "132" } });

  expect(getByLabelText("Add a new user category restraint")).toBeTruthy();

The fact that render works but renderWithRedux doesn't suggests the problem lies in the redux state management.

One thing to consider -- setting state queues a rerender within React, while dispatching an action to Redux queues the change in Redux's own update queue. React testing tools may not know how to deal with that.

Although it's closer to the code, you may want to consider mocking dispatch and verifying that it is receiving the expected results of the select changes. That way you can validate that your code is working, without worrying about what third party code is doing. (Kind of ironic that you were trying to avoid using react-select when renderWithRedux was the actual third-party problem. :D )

You may also want to consider directly calling your onChange handler, plucking it from the selects in your ponent instead of firing the events. This would be another way to reduce the amount of third-party code you are testing and mocking.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论