27

I am familiar with setting spies on Class or Object methods, but what about when the function is just an export default - such that the method itself is independent, like a utility?

I have some existing code like so:

const Funct1 = props => {
  if(props){
    Funct2(args);
  }
  // or return something
};
const Funct2 = props => {
  // do something
  return true
};
export default Funct1;  //Yes the existing export is named the same as the "entry" method above.

And, for example, I'd like to spy on Funct1 getting called and Funct2 returns true.

import Funct1 from "../../../src/components/Funct1";
describe("Test the Thing", () => {
    it("New Test", () => {
        let props = {
            active: true,
            agentStatus: "online"
        };
        const spy = spyOn(Funct2, "method name"); <-- how doe this work if not an obj or class?

        Funct1(props);
        //If I try Funct2(props) instead, terminal output is "Funct2 is not defined"

        expect(spy).toHaveBeenCalledWith(props);
    });
});
Phil Lucks
  • 2,704
  • 6
  • 31
  • 57
  • Possible duplicate of [How to spy on a default exported function](https://stackoverflow.com/questions/32891606/how-to-spy-on-a-default-exported-function) – Andrea Carraro Mar 24 '18 at 16:47
  • I noticed this had no accepted answer. Just fyi, I tried to `import * as functions` and then create a `const spy = jest.spyOn(functions, 'name')` -- but that did not work for me. – Cody Nov 23 '21 at 02:56

4 Answers4

28

I am not expert in jest, but my recommendation to think about:

1) When the function is exported as default I use something like:

import Funct1 from "../../../src/components/Funct1";
...
jest.mock("../../../src/components/Funct1");
...
expect(Funct1).toHaveBeenCalledWith(params);

2) When the module (utils.js) has multiple exports as

export const f1 = () => {};
...
export const f8 = () => {};

You can try

import * as Utils from "../../../src/components/utils"
const f8Spy = jest.spyOn(Utils, 'f8');
...
expect(f8Spy).toHaveBeenCalledWith(params);

Similar discussion here

Dima Dorogonov
  • 2,297
  • 1
  • 20
  • 23
12

Wrap your function with jest.fn. Like this:

const simpleFn = (arg) => arg;
const simpleFnSpy = jest.fn(simpleFn);
simpleFnSpy(1);
expect(simpleFnSpy).toBeCalledWith(1); // Passes test
callOfCode
  • 893
  • 8
  • 11
0

I think Jest requires mocks to be inside an Object or Class, but if they aren't like that in your code, you can still put them there in the test:

jest.mock('../pathToUtils', () => ({
  func2: jest.fun().mockImplementation((props) => "ok") //return what you want
  otherfuncs: ...etc
}));

const mockUtils = require('../pathToUtils')
const func1 = require('./pathToFunc1')

Then in the test:

func1()  // call the main function

expect(mockUtils.func2).toHaveBeenCalled
expect(mockUtils.func2).toHaveBeenCalledTimes(1)
expect(mockUtils.func2).toHaveBeenCalledWith(arg1, arg2, etc)
expect(mockUtils.func2).toHaveBeenLastCalledWith(arg1, arg2, etc)

You'll have already done unit testing of the Util function in another file, so you won't actually be running the function again here, this is just mocking for the purpose of the integration testing.

misterrodger
  • 216
  • 2
  • 8
-5

I believe that is not possible to test Funct1 calling Funct2 without modifying the existing code. If the latter is an option, here are two options to introduce dependency injection:

  • Create and export a factory function:

    const Funct2 = props => {
        // do something
        return true;
    };
    const Funct1 = CreateFunct1(Funct2);
    export function CreateFunct1(Funct2) {
        return props => {
            if (props) {
                Funct2(props);
            }
            // or return something
        };
    }
    export default Funct1;  
    
    // and here is the test:
    
    describe('Test the Thing', () => {
        it('New Test', () => {
            // Arrange
            const funct2Spy = jasmine.createSpy('Funct2');
            const funct1 = CreateFunct1(funct2Spy);
            const props = "some data";
    
            // Act
            funct1(props);
    
            // Assert
            expect(funct2Spy).toHaveBeenCalledWith(props);
        });
    });
    
  • Export Funct2 too. Here is a thread on this topic. Maybe its example would have to be tweaked a little bit for your scenario because of the export syntax.

Asen Arizanov
  • 930
  • 1
  • 9
  • 11