Nate
Nate

Reputation: 1750

SpyOn a mocked jest module not spying properly

I have simple service that I need unit tested using jest:

the crux of the code is this:

 domtoimage.toBlob(node, {filter: filter})
    .then(function (blob) {
      FileSaver.saveAs(blob, fileName);
 });

I have wrote my unit test module as such:

import FileSaver from "file-saver";
import domtoimage from "dom-to-image";


jest.mock('dom-to-image', () => {
  return {
    toBlob: (arg)=>{
      let promise = new Promise((resolve, reject) => {
        resolve('myblob')
      });
      return promise;
    }
  }
});
jest.mock('file-saver', ()=>{
  return {
    saveAs: (blob, filename) =>{
      return filename;
    }
  }
});

And in my test, I have the following spy set up

const spy = jest.spyOn(FileSaver, 'saveAs');

and calling my in-test function.

however, the expect statement: expect(spy).toBeCalled() returns false:

expect(jest.fn()).toBeCalled()

However, in webstorm, when I debug the unit test, I can clearly see that my mocked function is being called (the breakpoint is reached inside function).

What am i missing?

Upvotes: 11

Views: 12744

Answers (2)

Nate
Nate

Reputation: 1750

So for those of you wondering something similar...my issue (as I suspected) was the promise in domtoimage.toBlob(node, {filter: filter}).then()

essentially, the promise was resolving after my expect was called.

in order to solve it, I placed my expect behind a setTimeout, thus forcing it to fire after the promise is resolved.

 DownloadImage('x', 'name');
  //small timeout to resolve the promise inside downldimage function
  setTimeout(()=>{
    expect(FileSaver.saveAs).toHaveBeenCalledWith('myblob', fileName);
    done();
  }, 100);

Upvotes: 1

Red Mercury
Red Mercury

Reputation: 4310

Suggestion 1

Maybe spyOn and module mocks don't play well together. You could try using a jest.fn() directly inside the module mock like so

jest.mock('file-saver', ()=>{
  return {
    saveAs: jest.fn((blob, filename) => {
      return filename;
    })
  }
});

and then

expect(FileSaver.saveAs).toBeCalled()

Remember to call jest.clearAllMocks() or similar between tests.

Suggestion 2 I've had issues with jest.mock working in unexpected ways with the jest module cache, especially when working with singleton imports. Maybe you have this issue. if file-saver and dom-to-image don't have any state initialized or side-effects on import time you should be able to swap jest.mock out for overrides of the functions you need to mock.

beforeEach(() => {
  FileSaver.saveAs = jest.fn(...);
  domtoimage.toBlob = jest.fn(...);
})

Upvotes: 10

Related Questions