0

getInfo calls getColor in the same file. My intention is to mock getColor function, I import the func.js as a module and spyOn getColor. The mocked getColor is supposed to return "Red", but it still calls the actual function and returns "Black".

Function File

// func.js
function getColor() {
    return "black"
}

function getInfo(){
    const color = getColor()
    const size = "L"
    return `${color}-${size}`
}

module.exports = { getColor, getInfo }

Test File

// func.test.js
const func = require("./func")

describe("Coverage Change Test", () => {
  beforeAll(() => {
    const colorMock = jest.spyOn(func, "getColor"); // spy on otherFn
    colorMock.mockImplementation(() => "Red");
  });

  afterAll(() => {
    colorMock.resetAllMocks();
  })

  test("return Large Red", async () => {
    const res = func.getInfo();
    expect(res).toEqual("Red-L");
  });
});

I also tried requireActual, but it also calls the actual one.

const { getInfo, getColor } = require('./func');

jest.mock('./func', () => ({
  ...jest.requireActual('./func.'),
  getColor: jest.fn().mockImplementation(() => 'Red'),
}))

describe('test', () => {
  test('returns red', () => {
    const res = getInfo()
    expect(res).toEqual("Red-L")
  })
})

How can I properly mock up a nested function in Jest? Thanks in advance.

Luke
  • 468
  • 1
  • 5
  • 21
  • This is natural limitation of JS. getColor is local variable and cannot be modified outside the scope it was defined (module scope). Either treat these functions as 1 unit and test them altogether, or move them to separate modules, or consistently use them as methods on some object (`exports` like the answer suggested). – Estus Flask Feb 21 '21 at 07:08
  • @EstusFlask Thanks for the reply. if I have `const color = await getColor()`, how to export an await function? – Luke Feb 21 '21 at 07:51

2 Answers2

0

You're spying on func.getColor and mocking its implementation but getInfo is calling the locally scoped getColor function. You have to change the way getColor is being called to be able to mock it.

exports.getColor = function() {
  return 'black'
}

exports.getInfo = function() {
  const color = exports.getColor()
  const size = 'L'
  return `${color}-${size}`
}
Arun Kumar Mohan
  • 11,517
  • 3
  • 23
  • 44
  • Thanks for the reply. I am not allowed to change the function code itself. Can we achieve it in the test file? thank you. – Luke Feb 21 '21 at 05:39
  • @Luke That's weird. You could use [rewire](https://github.com/jhnns/rewire) to override the `getColor` function in the test. – Arun Kumar Mohan Feb 21 '21 at 05:49
  • thank you so much. I guess my intention is I don't want to make a change to the code in order to create a unit test. – Luke Feb 21 '21 at 05:53
  • Rewire isn't supposed to be workable in Jest, they both hack module API and compete over it. – Estus Flask Feb 21 '21 at 07:04
  • @Luke You should be cause you wrote regardless of how it will be tested, and this needs to be fixed. – Estus Flask Feb 21 '21 at 07:07
-1

Use rewire module to rewrite your exported properties.

Your test file will look like this:

const rewire = require('rewire')

describe('test', () => {
  let func;
  let getColorSpy;
  beforeEach(() => {
    getColorSpy = jest.fn()
    func = rewire(__dirname + '/func.js') // import
    func.__set__('getColor', getColorSpy) // rewrite content of getColor
  })
  test('should return Red-L when getColor returns Red', () => {
    getColorSpy.mockReturnValue('Red')

    const res = func.getInfo()

    expect(res).toEqual("Red-L")
  })
})

Try to avoid write code like your module.

hoangdv
  • 15,138
  • 4
  • 27
  • 48