Reputation: 2541
Component to test -
import { useState } from "react";
const UserForm = ({ onUserAdd }) => {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
onUserAdd({ name, email });
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>Name</label>
<input value={name} onChange={(e) => setName(e.target.value)} />
</div>
<div>
<label>Email</label>
<input value={email} onChange={(e) => setEmail(e.target.value)} />
</div>
<button onClick={handleSubmit}>Add User</button>
</form>
);
};
export default UserForm;
Failing Test Case -
test("it calls onUserAdd when the form is submitted", async () => {
// BETTER IMPLEMENTATION
const mock = jest.fn();
// Render - the component
render(<UserForm onUserAdd={mock} />);
// Find 2 inputs (name, email in this case)
const [nameInput, emailInput] = screen.getAllByRole("textbox");
// Simulate typing in a name
await user.click(nameInput);
await user.keyboard("jane");
// Simulate typing in a email
await user.click(emailInput);
await user.keyboard("[email protected]");
// Find the submit button
const button = screen.getByRole("button");
// Simulate clicking the submit button
await user.click(button);
// Assertion - make sure 'onUserAdd' gets called with name/email
expect(mock).toHaveBeenCalled();
expect(mock).toHaveBeenCalledWith({ name: "jane", email: "[email protected]" });
});
Error -
- Expected
+ Received
Object {
- "email": "[email protected]",
- "name": "jane",
+ "email": "",
+ "name": "",
},
Let me know what I am doing wrong here.
Codesandbox Link - https://codesandbox.io/s/morning-microservice-xxh0mw?file=/src/UserForm.test.js
PS - Since I am learning react testing, I used await
for every event, let me know if this is a correct approach as well. Thanks.
Upvotes: 0
Views: 348
Reputation: 1407
Generally looks ok but I find CodeSandbox to be really buggy when dealing with react testing library.
I replaced your user.click
s and user.keyboard
s with user.type
which I find more human readable and concise to work with and it seems to work:
test("it calls onUserAdd when the form is submitted", async () => {
const mock = jest.fn();
// Render - the component
render(<UserForm onUserAdd={mock} />);
// Find 2 inputs (name, email in this case)
const [nameInput, emailInput] = screen.getAllByRole("textbox");
// Simulate typing in a name
await user.type(nameInput, "jane"); // <--here-<<
// Simulate typing in a email
await user.type(emailInput, "[email protected]"); // <--and here-<<
// Find the submit button
const button = screen.getByRole("button");
// Simulate clicking the submit button
await user.click(button);
// Assertion - make sure 'onUserAdd' gets called with name/email
expect(mock).toHaveBeenCalled();
expect(mock).toHaveBeenCalledWith({ name: "jane", email: "[email protected]" });
});
And yes, you are correct wo await
every user action. Although RTL docs recommend to use the new setup const user = userEvent.setup()
instead of calling the API directly
Upvotes: 1