问题
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