DEV Community

Edwin Wong
Edwin Wong

Posted on

7 3

How to mock function that called inside module

Problem

Iets say you have the file

// file.js

export function b() {
    return 'B'
}

export function a() {
    return b()
}

module.exports = {
    a,
    b,
}
Enter fullscreen mode Exit fullscreen mode

function a is calling function b internally. To mock the function b can be very difficult.

You will probably do something like this in your test

Using jest.mock method

jest.mock('./file', () => {
    const original = jest.requireActual('./file')
    return {
        ...orignial,
        b: jest.fn()
    }
})

const f = require('./file')

test('a', () => {
    f.b.mockReturnValue('C')

    expect(f.a()).toBe('C')
    // this will failed, it got 'B'
})
Enter fullscreen mode Exit fullscreen mode

Using jest.spyOn method

const f = require('./file')

test('a', () => {
    jest.spyOn(f, 'b').mockReturnValue('C')

    expect(f.a()).toBe('C')
    // it sill failed!, it got 'B'
})
Enter fullscreen mode Exit fullscreen mode

This is not a bug, above 2 methods are working fine. The main reason is because the reference point. Once the function is mocked and print it out you will see something like this.

[Function: b] {
    _isMockFunction: true,
    getMockImplementation: [Function (anonymous)],
    mock: [Getter/Setter],
    mockClear: [Function (anonymous)],
    mockReset: [Function (anonymous)],
    mockRestore: [Function (anonymous)],    
    ...
    ...
}
Enter fullscreen mode Exit fullscreen mode

Now you try print out the function b that called in function a. And, run the test again.

export function a() {
    console.log(b) // it will print [Function: b] (without the mock property)
    return b()
}
Enter fullscreen mode Exit fullscreen mode

Solution 1

Move function b to another file.

// b.js
export function b() {
    return 'B'
}
Enter fullscreen mode Exit fullscreen mode
// file.js
import {b} from "./b"

export function a() {
    return b()
}
Enter fullscreen mode Exit fullscreen mode

In this case, just mock b will do. I believe you know better than me.

Solution 2

Using the same reference point. This could be abit ugly to you codebase. I think is fine for me.

// file.js

export function b() {
    return 'B'
}

export function a() {
    return module.exports.b() // magic here
}

module.exports = {
    a,
    b,
}
Enter fullscreen mode Exit fullscreen mode

Or you can do something like, if this is the way you define your module.

// file.js

module.exports = {
    b: () => {
        return 'B'
    },
    a: () => {
        return this.b() // this magic
    }
}
Enter fullscreen mode Exit fullscreen mode

Both are achive the same result and same principle.

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (2)

Collapse
 
jackmellis profile image
Jack

Solution 3 is to use a dependency injection or composition pattern to give you more control over your dependencies 👀

Collapse
 
edwinwong90 profile image
Edwin Wong

Yeah~ true.

SurveyJS custom survey software

JavaScript Form Builder UI Component

Generate dynamic JSON-driven forms directly in your JavaScript app (Angular, React, Vue.js, jQuery) with a fully customizable drag-and-drop form builder. Easily integrate with any backend system and retain full ownership over your data, with no user or form submission limits.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay