5

I'm struggling with using spyOn as part of testing my utils.js module. I've tried various methods and approaches but all seem to yield the "expected mock function to have been called". For the record, other unit tests work OK, so there shouldn't be any issue with my actual test setup.

Below is a simplified test case with two functions and one test, and I can't even get these to work. Did I misunderstand the spyOn altogether?

// utils.js
function capitalHelper(string){
  return string.toUpperCase();
}

function getCapitalName(inputString){
  return capitalHelper(inputString.charAt(0)) + inputString.slice(1);
}

exports.capitalHelper = capitalHelper
exports.getCapitalName = getCapitalName



// utils.test.js
const Utils = require('./utils');

test('helper function was called', () => {
  const capitalHelperSpy = jest.spyOn(Utils, 'capitalHelper');
  const newString = Utils.getCapitalName('john');
  expect(Utils.capitalHelper).toHaveBeenCalled();
})
brass monkey
  • 5,841
  • 10
  • 36
  • 61
terjeofnorway
  • 53
  • 1
  • 1
  • 4

1 Answers1

3

I do ont use spyOn(), but jest.fn() instead for all mock scenario

In your case I would do the following

test('helper function was called', () => {
    Utils.capitalHelper = jest.fn((s) => Utils.capitalHelper(s))
    const newString = Utils.getCapitalName('john')
    expect(Utils.capitalHelper.mock.calls.length).toBe(1)
})

First line could have simply be :

Utils.capitalHelper = jest.fn()

since you don't seem to be testing the returned value in your test :)

You can find more details on jest.fn() on the jest official documentation : https://facebook.github.io/jest/docs/en/mock-functions.html

----------------------- EDIT

I got it : the problem occurs because within your utils.js file, getCapitalName uses the defined function, not the one pointed by the export.

To be able to mock the function in use you could change your utils.js file to

// utils.js
const Utils = {
    capitalHelper: string => string.toUpperCase(),
    getCapitalName: inputString => Utils.capitalHelper(inputString.charAt(0)) + inputString.slice(1)
}

export default Utils

then the tests I gave before will work

Ji aSH
  • 3,206
  • 1
  • 10
  • 18
  • 1
    I removed value testing for simplicity when posting my question. However, I can't get your suggestion to work either. It says calls.length is 0. I'll just have to look around some more and see if I can find a solution. Will post it here for anyone interested. – terjeofnorway Jun 06 '18 at 19:00
  • Thanks for your update! I did get this to work with a small alteration in utils.test.js: const Utils = require('./utils').default; However, I also found that splitting the two into separate js files worked as well, so the problem seems to be a sort of conflict where Jest is trying to mock one function and run another functions logic all in the same module. For anyone interested, I've pasted my alternative code below. Thanks so much for your answer! – terjeofnorway Jun 07 '18 at 21:05
  • `// utils.js import * as Helpers from './helpers'; const firstName = (name) => { return Helpers.prettyFirstName(name); } export { firstName } // helpers.js const prettyFirstName = (string) => { return string.charAt(0).toUpperCase() + string.substr(1, string.length - 1); } export { prettyFirstName }` – terjeofnorway Jun 07 '18 at 21:07
  • Then, finally the test itself: ´import * as Utils from './utils'; import * as Helpers from './helpers'; test('testing name', () => { expect(Utils.firstName('dan')).toEqual('Dan'); }) test('testing name spy', () => { Helpers.prettyFirstName = jest.fn(); const name = Utils.firstName('dan'); expect(Helpers.prettyFirstName).toHaveBeenCalled(); })´ – terjeofnorway Jun 07 '18 at 21:08
  • This answer works, but didn't technically answer the OP question. Using spyOn is a valid option, and toHaveBeenCalled() can be asserted. – mojave Mar 07 '19 at 17:52