简体   繁体   中英

How to test asynchronous function with rtl and jest in React

I'm trying to test my asynchronous submit function on my form using RTL and Jest but encounter some errors along the way. By the way, this is my first time testing my code so I'm quite confused about everything regarding the testing procedures.

Here is my error:

expect(jest.fn()).toHaveBeenCalled()

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

  34 |
  35 |         fireEvent.submit(getByTestId('submitBtn'))
> 36 |         expect(submitTodo).toHaveBeenCalled()
     |                            ^
  37 |     })
  38 | })

Below is my code that checks my form.

it('checks the function submitTodo', () => {
    const submitTodo = jest.fn();
    const { getByTestId } = render(<InputField submitTodo={submitTodo} />);
    const input = getByTestId('input');
    fireEvent.change(input, { target: { value: 'test' } })

    fireEvent.submit(getByTestId('submitBtn'))
    expect(submitTodo).toHaveBeenCalled()
})

and here is my form alongside with my function.

import { useState } from 'react';
import { firestore } from 'firebase/firebaseConfig';
import firebase from 'firebase/firebaseConfig';
import styles from 'theme/main.module.scss';

const InputField = () => {
  const [todo, setTodo] = useState('');

  const submitTodo = async (e) => {
    e.preventDefault();
    try {
      await firestore.collection('todo').add({
        todo,
        timestamp: firebase.firestore.FieldValue.serverTimestamp(),
      });
    } catch (error) {
      alert(error);
    }
    setTodo('');
  };
  return (
    <form
      data-testid='form'
      className={styles.inputFieldContainer}
      onSubmit={(e) => submitTodo(e)}
    >
      <input
        data-testid='input'
        className={styles.inputFieldContainer__input}
        placeholder='Please enter a todo.'
        required
        value={todo}
        onChange={(e) => setTodo(e.target.value)}
      />
      <button
        data-testid='submitBtn'
        className={styles.inputFieldContainer__btn}
        type='submit'
      >
        Submit
      </button>
    </form>
  );
};

export default InputField;

This issue happens usually when there is some async action within the code that is being tested. So the test completes before the async method returns.

Jest has many ways to test async functions mentioned in their documentation. But one of the straightforward ways is to use the done callback.

So your code for the test would be like

it('checks the function submitTodo', (done) => {
    const submitTodo = jest.fn();
    const { getByTestId } = render(<InputField submitTodo={submitTodo} />);
    const input = getByTestId('input');
    fireEvent.change(input, { target: { value: 'test' } })

    expect.assertions(1);

    fireEvent.submit(getByTestId('submitBtn'))
    expect(submitTodo).toHaveBeenCalled()
    done();    
})

The done() callback ensures that jest will wait until the done callback is made.Also the expect.assertions ensures that the assertion is actually made and fails if the assertion is not made.

More details and documentation can be found from the official jest docs

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM