Using react-hooks-testing-library with jest.spyOn - spy is not called

Scott picture Scott · Jun 5, 2019 · Viewed 7.3k times · Source

I am having an issue setting up a unit test to determine that a function is called with the correct arguments. useAHook returns function foo which calls function bar. The code looks like this

//myModule.js
export const useAHook = (arg1, arg2) => {
  const foo = useCallback(() => {
    bar(arg1, arg2);
  }, [arg1, arg2]);

  return foo;
}

export const bar = (a, b) => {
   //does some stuff with a and b
}

I am trying to unit test this code using renderHook and jest.spyOn. I want to confirm that calling function foo results in bar being called with the correct arguments. My unit test looks like this

//myModule.spec.js

import * as myModule from './myModule.js'

it('should call foo with correct arguments', () => {
  const spy = jest.spyOn(myModule, 'bar');
  const { result } = renderHook(() => myModule.useAHook('blah', 1234));
  const useAHookFunc = result.current;

  useAHookFunc();

  // fails, spy is not called
  expect(spy).toBeCalledWith('blah', 1234);
});

The result is that the test fails saying that spy is never called. Am I doing something wrong here or using either tool incorrectly?

Answer

Brian Adams picture Brian Adams · Jun 6, 2019

This line:

import * as myModule from './myModule.js'

...imports the module bindings for myModule.js into myModule.

Then this line:

const spy = jest.spyOn(myModule, 'bar');

...wraps the module export for bar in a spy...

...but the spy never gets called because useAHook doesn't call the module export for bar, it just calls bar directly.


If you modify useAHook to call the module export for bar then the spy will get called.

There are a couple of ways to do that.

You can move bar into its own module...

...or you can import the module bindings for myModule.js so you can call the module export for bar:

import { useCallback } from 'react';

import * as myModule from './myModule';  // <= import the module bindings

export const useAHook = (arg1, arg2) => {
  const foo = useCallback(() => {
    myModule.bar(arg1, arg2);  // <= call the module export for bar
  }, [arg1, arg2]);

  return foo;
}

export const bar = (a, b) => {
   //does some stuff with a and b
}