简体   繁体   中英

Testing a callback function in Jest

Needless to say, the app is a lot more complicated that this, but the gist is the same. I cannot make major changes to the App, such as import/export the onSuccess function or changing to a class based component.

A button click kicks off the Login function, where I pass in an onSuccess function. I want to spy on the analyze function to make sure it is called, but in my test I have no way of calling the onSuccess function

Ideally I would like to do something like this:

test("analyze should be called", () => {
  let analyzeSpy = jest.spyOn(Analytics, "analyze");
  onSuccess() //<- cannot do this
  expect(analyzeSpy).toHaveBeenCalledTimes(1);
});

Here is the app:

import Analytics from "./Analytics";

export function Login({ onLoginSuccess }) {
  setTimeout(function () {
    console.log("TIMEOUT OVER");
    onLoginSuccess();
  }, 2000);
}

function App() {
  function handleClick() {
    console.log("Login");
    Login({
      onLoginSuccess: onSuccess,
    });
  }
  function onSuccess() {
    console.log("Login success");
    Analytics.analyze();
  }
  return (
    <>
      <button
        onClick={() => {
          handleClick();
        }}
      >
        Login
      </button>
    </>
  );
}

export default App;

This is Analytics.js:

export default {
  analyze: () => {
    console.log("Analysis done");
  },
};

How can I test this?

Use render() method of react-dom module to render your component into document provided by js-dom .

Use document.querySelector('button') to get the button dom, dispatch a mouse click event.

Use jest.useFakeTimers() to use fake versions of the standard timer functions( setTimeout ).

After dispatching the click event, use jest.advanceTimersByTime(2000) to executes the macro task queue (tasks queued by setTimeout ).

Then, make an assertion.

Eg

App.jsx :

import React from 'react';
import Analytics from './Analytics';

export function Login({ onLoginSuccess }) {
  setTimeout(function () {
    console.log('TIMEOUT OVER');
    onLoginSuccess();
  }, 2000);
}

function App() {
  function handleClick() {
    console.log('Login');
    Login({
      onLoginSuccess: onSuccess,
    });
  }
  function onSuccess() {
    console.log('Login success');
    Analytics.analyze();
  }
  return (
    <>
      <button
        onClick={() => {
          handleClick();
        }}
      >
        Login
      </button>
    </>
  );
}

export default App;

App.test.jsx :

import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { act } from 'react-dom/test-utils';
import App from './App';
import Analytics from './Analytics';

describe('68400320', () => {
  let container = null;
  beforeEach(() => {
    // setup a DOM element as a render target
    container = document.createElement('div');
    document.body.appendChild(container);
  });

  afterEach(() => {
    // cleanup on exiting
    unmountComponentAtNode(container);
    container.remove();
    container = null;
  });

  test('should pass', () => {
    const analyzeSpy = jest.spyOn(Analytics, 'analyze');
    jest.useFakeTimers();
    act(() => {
      render(<App />, container);
    });
    const button = document.querySelector('button');
    act(() => {
      button?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
      jest.advanceTimersByTime(2000);
    });
    expect(analyzeSpy).toBeCalledTimes(1);
  });
});

test result:

PASS  examples/68400320/App.test.jsx (8.12 s)
  68400320
    ✓ should pass (52 ms)

  console.log
    Login

      at handleClick (examples/68400320/App.jsx:13:13)

  console.log
    TIMEOUT OVER

      at examples/68400320/App.jsx:6:13

  console.log
    Login success

      at onSuccess (examples/68400320/App.jsx:19:13)

  console.log
    Analysis done

      at Object.analyze (examples/68400320/Analytics.js:3:13)

--------------|---------|----------|---------|---------|-------------------
File          | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
--------------|---------|----------|---------|---------|-------------------
All files     |     100 |      100 |     100 |     100 |                   
 Analytics.js |     100 |      100 |     100 |     100 |                   
 App.jsx      |     100 |      100 |     100 |     100 |                   
--------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        8.666 s, estimated 10 s
Ran all test suites related to changed files.

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