'mock function error in react-testing-library

Hii Everyone I am new to react testing ,I am trying to do some example for practise , I am getting a Error,Need your help , this is my App Component

  const[firstName,setFirstName]=useState("")
  const[lastName,setLastName]=useState("")
  const [data,setData] = useState({})

  const handleFirstName = (e) =>{
    setFirstName(e.target.value)
  }
  const handleLastName = (e) =>{
    setLastName(e.target.value)
  }
  
  const handleSubmit = (e) =>{
    e.preventDefault();
    setData({firstName,lastName})
    console.log(firstName,lastName)
  }

  return (
    <div className="App">
     <form onSubmit={handleSubmit} data-testid="form" >
       <div>
         <label>FirstName
         <input type="text" name="firstName" id="firstName" value={firstName} onChange={handleFirstName}/>
         </label>
       </div>
       {firstName && firstName.length > 10 && <p data-testid="error-msg" >FirstName is not valid</p>}
       <div>
         <label>lastName
         <input type="text" name="lastName" id="lastName" value={lastName} onChange={handleLastName}/>
         </label>
       </div>
       <button type="submit" name="submit" disabled={firstName === ""} >submit</button>
     </form>
    </div>
  );
}

this is my testing logic

  const mockFunction = jest.fn();
  const {getByText}=render(<App onSubmit={mockFunction}/>)
  const firstNameLabel = screen.getByRole("textbox",{'name':'FirstName'})
  fireEvent.change(firstNameLabel,{"target":{'value':"dhffssß"}})
  const lastNameLabel = screen.getByRole("textbox",{"name":"lastName"})
  fireEvent.change(lastNameLabel,{"target":{'value':"dhfffsß"}})
  const btn = screen.getByRole('button',{'name':'submit'})
  fireEvent.click(btn)
  expect(mockFunction).toHaveBeenCalledTimes(1)
 })

I am testing simple form but getting this error


expect(jest.fn()).toHaveBeenCalledTimes(expected)

Expected number of calls: 1
Received number of calls: 0



Solution 1:[1]

Try to call onSubmit function which you pass in props in your handleSubmit handler. In this case you should be able to track if the onSubmit callback was called in your tests.

const App = ({ onSubmit }) => {
  const[firstName,setFirstName]=useState("")
  const[lastName,setLastName]=useState("")
  const [data,setData] = useState({})

  const handleFirstName = (e) =>{
    setFirstName(e.target.value)
  }
  const handleLastName = (e) =>{
    setLastName(e.target.value)
  }
  
  const handleSubmit = (e) =>{
    e.preventDefault();
    setData({firstName,lastName});
    console.log(firstName,lastName);
    onSubmit();
  }

  return (
    <div className="App">
     <form onSubmit={handleSubmit} data-testid="form" >
       <div>
         <label>FirstName
         <input type="text" name="firstName" id="firstName" value={firstName} onChange={handleFirstName}/>
         </label>
       </div>
       {firstName && firstName.length > 10 && <p data-testid="error-msg" >FirstName is not valid</p>}
       <div>
         <label>lastName
         <input type="text" name="lastName" id="lastName" value={lastName} onChange={handleLastName}/>
         </label>
       </div>
       <button type="submit" name="submit" disabled={firstName === ""} >submit</button>
     </form>
    </div>
  );
}
}

Solution 2:[2]

You may need to wait until the submit button is enabled before trying to interact with it, after you changed the firstName input, and wait for the callback call (not sure the second wait is usefull) :

import { render, waitFor } from '@testing-library/react';

const mockFunction = jest.fn();
const {getByText}=render(<App onSubmit={mockFunction}/>)
const firstNameLabel = screen.getByRole("textbox",{'name':'FirstName'})
fireEvent.change(firstNameLabel,{"target":{'value':"dhffssß"}})
const lastNameLabel = screen.getByRole("textbox",{"name":"lastName"})
fireEvent.change(lastNameLabel,{"target":{'value':"dhfffsß"}})

// wait until button is enabled
await waitFor(() => expect(getByText('submit').toBeEnabled());

fireEvent.click(getByText('submit'));
await waitFor(() => expect(mockFunction).toHaveBeenCalled();

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Georgy
Solution 2 Florian Motteau