1

I'm trying to mock one specific function when using module.exports. How could I test the inner function B?

In my worker.js

module.exports = function () {
  this.funcA = funcA
  this.funcB = funcB
}

funcA () {
  funcB()
}

funcB() {...}

In my worker-test.js

const Worker = require('./worker')

test('test functionB', () => {...}) 

test('test functionA', () => {
  const work = new Worker()
  work.funcB = jest.fn()  //mock funcB
  work.funcA  //run funcA

  expect(work.funcB).toHaveBeenCalledTimes(1) //Error
}) 

I'm new to jest. Is there any good way to mock function in this case?

taian.chen
  • 163
  • 1
  • 1
  • 5
  • https://jestjs.io/docs/en/mock-functions#mocking-modules – Dom Apr 24 '19 at 03:24
  • @Dom I have read this, but i still have no idea in my case. :( – taian.chen Apr 24 '19 at 04:04
  • 1
    @Dom - That doesn't help. This is a common problem. – dwjohnston Apr 24 '19 at 05:37
  • I went through this a while back. If you're interested - I summarised some things here: https://stackoverflow.com/questions/53736766/what-is-the-state-of-the-art-for-testing-mocking-functions-within-a-module-in-20 – dwjohnston Apr 24 '19 at 05:38
  • And see the accepted answer for this question https://stackoverflow.com/questions/54318830/why-does-mutating-a-module-update-the-reference-if-calling-that-module-from-anot – dwjohnston Apr 24 '19 at 05:39
  • @dwjohnston Thanks for these helpful links. The summarised one is very clear to me. – taian.chen Apr 24 '19 at 07:04

2 Answers2

2

There isn't a way to mock funcB the way the code is currently written since funcA calls funcB directly.

The easiest way to fix it is to note that worker.js returns a constructor function and funcA and funcB are almost prototype methods...

...if you make them prototype methods then funcB can be mocked:

worker.js

class Worker {
  funcA() {
    this.funcB();
  }
  funcB() {
    throw new Error('should not make it here');
  }
}

module.exports = Worker;

worker.test.js

const Worker = require('./worker');

test('test functionB', () => { /* ... */ })

test('test functionA', () => {
  const spy = jest.spyOn(Worker.prototype, 'funcB');  // <= spy on funcB
  spy.mockImplementation(() => {});  // <= mock funcB

  const work = new Worker();
  work.funcA();  // <= call funcA

  expect(spy).toHaveBeenCalledTimes(1);  // Success!
  spy.mockRestore();  // <= restore funcB
}) 
Brian Adams
  • 43,011
  • 9
  • 113
  • 111
  • I try to break all my function into modules. Small module is easy to mock, but I think that I should rewrite my worker.js with class. Thanks! – taian.chen Apr 26 '19 at 01:57
0

I know this is an old question but I thought I would chime in as I was searching for a way to do this as well and have discovered it is in fact possible.

Rather than calling the javascript function as above, you'll need to give it the scope of this to ensure that when you mock funcB, funcA calls the mocked version rather than just the function itself.

This means worker.js becomes

module.exports = function () {
  this.funcA = funcA
  this.funcB = funcB
}

funcA () {
  this.funcB()
}

funcB() {/* Your impl */}

And worker.test.js can remain, as before:

const Worker = require('./worker')

test('test functionB', () => {...}) 

test('test functionA', () => {
  // You could even just have: const work = require('./worker')
  const work = new Worker()
  work.funcB = jest.fn()  //mock funcB
  work.funcA()  //run funcA

  expect(work.funcB).toHaveBeenCalledTimes(1)
}) 
tDickinson
  • 37
  • 3