Testing parent methods in stateless child component in Jest

假如想象 提交于 2021-02-19 07:35:14

问题


I currently have a react component with an onChange method.

I want to test the onChange method like if someone were to type in, or paste things in. The problem is the handleChange is dependent on the parent components using it. What's the best way to test for the simulation? Do I need to reference the parent component's handleChange method and simulate that?

CodeSandbox (i didn't add the tests here)

https://codesandbox.io/s/react-basic-example-cwdh1?from-embed

Updated Current Test:

const handleChange = jest.fn();

const initProps = {
  name: "Title",
  label: "Body",
  type: "text",
  value: "",
  handleChange
};

describe("TextBox", () => {
    let wrapper;
    beforeEach(() => {
      wrapper = mount(<TextBox {...props} />);
    });

    afterEach(() => {
      // resets the mocked fn after each test
      handleChange.mockClear();
    });

    it("should call handleChange with a new value", () => {
      const value = "testing input";
      wrapper.find("input").simulate("change", { target: { value } });

      expect(handleChange).toHaveBeenCalledWith(value);
    });
  });

Error:

Expected: "testing input"
Received: {"_dispatchInstances": null, "_dispatchListeners": null,
 "_targetInst": ......more fields... "nativeEvent": {"target": <input … />,
 "type": "change"}, "target": {"value": "testing input"}, "timeStamp":
 1576040322012, "type": "change"}

I know I'm getting this error because the value is coming out of the formfields. Is there a way I can extract the target field out?


回答1:


I think you're a little confused about your testing methods. What you're trying to do is a component/integration test with a parent and child component (manipulating a parent class field to update a child's value prop), when what you actually have is an isolated reusable TextBox child component without a parent, which requires a unit test.

So you have two options:

Option 1: Mount the parent component and manipulate its class fields to update this child component (and/or manipulate the child's event handler props which then updates the parent's state... and so on). This will test synchronicity between a parent and its child for that ONE particular component. This is especially useful for testing a component (like a form) that manages state and allocates it to some children (form elements) components.

Option 2: Shallow/Mount the child component and manipulate its event handlers to trigger jest mocked parent props (like manipulating an input's onChange prop and expecting it to call a mocked parent handleChange prop with a value). It doesn't actually update the child but simulates it. This is especially useful for testing SHARED reusable child components that can have many different parent components.

Which option to choose will vary from dev to dev and project to project. But in an ideal world, your testing suites will follow something similar to the "testing pyramid":

In your case, I'd recommend that if this child component is shared with many parent components across your app, then do both parent integration tests and a child unit test (this will cause some overlapping tests, but ensures compatibility across the app if either a parent or the child were to change or be updated). If this component is only being reused within a single parent component, then just do a single parent integration test. That said, this just my recommendation -- not a rule!


On a separate note, your TextBox component doesn't utilize state, so instead of using a class, you can simply use a stateless function:

const TextBox = ({ value, type, handleChange }) => (
  <Form.Control
    type={type}
    value={value}
    onChange={handleChange}
  />
);

Here's an example of how you can test this component:

Some notes: Your TextBox is essentially a third party component that has already, presumably, been tested by the react-bootstrap maintainers. So from a practical standpoint, creating a test for this child component is redundant. Instead, I'd recommend testing the parent component and expecting the parent's state to update when this child TextBox's input has been changed. Nevertheless, the codesandbox includes tests for both a TextBox and a Form component. In regards to why your handleChange mocked function was being called with multiple props... it's being called with a Synthetic Event, which is normal (my brain fart for not taking this into account). That said, you can assert against the Synthetic Event by using expect.objectContaining by either traversing the mockedFn.mock.calls arrays or by asserting against the mockedFn itself (both are included in the TextBox tests).



来源:https://stackoverflow.com/questions/59256324/testing-parent-methods-in-stateless-child-component-in-jest

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!