I'm trying to learn react-testing-library but I have a failure here that I'm sure should work and can't find the error.
It's a simple name and email app where you add a name and email and it is added to a list.
The home has a UserFrom and UserList "use client"
import UserForm from './components/UserForm'
import { useState } from 'react'
import UserList from './components/UserList'
export interface User{
name: string,
email: string
}
export default function Home() {
const [users, setUsers] = useState<User[]>([])
const onUserAdd = (user:User) => {
setUsers([...users, user])
}
return (
<>
<h1>RTL App</h1>
<UserForm onUserAdd={onUserAdd}/>
<hr />
<UserList users={users}/>
</>
)
}
User Form take the name and email and passed it back to home
"use client"
import React, { useState } from 'react'
const UserForm = ({ onUserAdd }: any) => {
const [email, setEmail] = useState('')
const [name, setName] = useState('')
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
onUserAdd({ name, email })
}
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="name">Name</label>
<input id="name" type="text" value={name} onChange={(e) => setName(e.target.value)} />
</div>
<div>
<label htmlFor="email">Email</label>
<input id="email" type="text" value={email} onChange={(e) => setEmail(e.target.value)} />
</div>
<button>Add User</button>
</form>
)
}
export default UserForm
UserList then displays the name and email
"use client"
import React from 'react'
const UserList = ({users}:any) => {
const renderUsers = users.map((user:any) => {
return(
<tr key={user.name}>
<td>{user.name}</td>
<td>{user.email}</td>
</tr>
)
})
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
{renderUsers}
</tbody>
</table>
)
}
export default UserList
The test are simply to test there are two inputs and a button and test the Useradd function and that the name and email are passed
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import UserForm from './UserForm'
test('it show two inputs and a button', () => {
render(<UserForm />);
const inputs = screen.getAllByRole('textbox');
const button = screen.getByRole('button');
expect(inputs).toHaveLength(2);
expect(button).toBeInTheDocument();
});
test('it calls onUserAdd when form submitted', () => {
const mock = jest.fn();
render(<UserForm onUserAdd={mock} />);
const nameInput = screen.getByRole('textbox', { name: /name/i });
const emailInput = screen.getByRole('textbox', { name: /email/i });
userEvent.type(nameInput, 'jane');
userEvent.type(emailInput, 'jane@jane.com');
const button = screen.getByRole('button');
userEvent.click(button);
expect(mock).toHaveBeenCalled();
expect(mock).toHaveBeenCalledWith({ name: 'jane', email: 'jane@jane.com' });
});
The error I get says the mock function isn't called but I can't work out why.
Can anyone see what I'm missing
✓ it show two inputs and a button (84 ms) ✕ it calls onUserAdd when form submitted (41 ms)
● it calls onUserAdd when form submitted
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
23 | userEvent.click(button);
24 |
> 25 | expect(mock).toHaveBeenCalled();
| ^
26 | expect(mock).toHaveBeenCalledWith({ name: 'jane', email: 'jane@jane.com' });
27 | });
28 |
If you are using @testing-library/user-event
version >= 14, please use await
before any API. There are some examples on official doc
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';
import UserForm from './UserForm';
test('it show two inputs and a button', () => {
render(<UserForm />);
const inputs = screen.getAllByRole('textbox');
const button = screen.getByRole('button');
expect(inputs).toHaveLength(2);
expect(button).toBeInTheDocument();
});
test('it calls onUserAdd when form submitted', async () => {
const mock = jest.fn();
render(<UserForm onUserAdd={mock} />);
const nameInput = screen.getByRole('textbox', { name: /name/i });
const emailInput = screen.getByRole('textbox', { name: /email/i });
await userEvent.type(nameInput, 'jane');
await userEvent.type(emailInput, 'jane@jane.com');
const button = screen.getByRole('button');
await userEvent.click(button);
expect(mock).toHaveBeenCalled();
expect(mock).toHaveBeenCalledWith({ name: 'jane', email: 'jane@jane.com' });
});
Test result:
PASS stackoverflow/77369394/UserForm.test.tsx
✓ it show two inputs and a button (54 ms)
✓ it calls onUserAdd when form submitted (112 ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 1.266 s, estimated 7 s
Ran all test suites related to changed files.
package version:
"@testing-library/user-event": "^14.4.3",
Related question: userEvent in React Testing Library Doesn't Cause onClick To Be Called