'Mocking jQuery $() and $ functions in jest

I have a set of tests for the functions in an ES6 class that use $.get(). I was able to mock $.get(). I'm working on testing another function in the same class that uses $.get() and $(data, ownerDocument).find(), and I haven't been able to figure out how to add mocking for the $().find() function.

How I'm mocking $.get():

const jQuery = require(path/to/jquery);
jest.mock(path/to/jquery);

describe("Description", () => {
  test("Test", () => {
    // multiple tests with different mocks of $.get, so clearing
    jQuery.get.mockClear();
    jQuery.get.mockImplementation((path) => path);
    // test function that uses $.get
  });
});

Based on this section from the jest docs, I tried adding mocking for the jQuery constructor by using the 2 argument version of jest.mock:

// jest.mock(path/to/jquery);
jest.mock(path/to/jquery, () => {
  return jest.fn().mockImplementation(arg1, arg2) => {
    return {
      find: () => console.log('find'); // just to see if structure works
    };
  });
});

describe("Description", () =>
  test("Test", () => {
    jQuery.get.mockClear();
    jQuery.get.mockImplementation((path) => path);

    // test function that uses $.get() and $(...).find()
  });
});

However, adding this broke the mocking of $.get() and I started getting this error:

TypeError: cannot read property 'mockClear' of undefined
  jQuery.get.mockClear()
             ^

Based on this article, what I believe I'm struggling with is the right way to mock functions in the $ namespace and the $.fn namespaces at the same time.

This question talks about how to mock $.ajax, and I do something like that to mock $.get but I'm not sure if it would help me mock $(...).find() as well.

Is there a way I can cleanly mock both functions I need simultaneously?

(As a side note, I'm not sure how I would create a Minimally Reproducible Example with jest, but if someone can point me to a way to do that, I'll make an MRE)



Solution 1:[1]

For future reference, I was able to resolve this by modifying the technique in the "Mock jQuery globally" portion of the accepted answer on this question. My resulting mock code looks like this:

//__mocks__/jquery.js:

const jQ = jest.requireActual("jquery");

const get = jest.fn((path) => {
    return Promise.resolve(path);
});

const when = jest.fn((prom1, prom2, prom3, prom4) => {
    return Promise.resolve();
});

const jQuery = jQ;
jQuery.get = get;
jQuery.when = when;
exports.jQuery = jQuery;

As I understand it, this is essentially a pre-ES6 version of the code provided in that answer. I'm using it as follows. I don't have to worry about mocking $().find because the mock uses the real implementation of the find function.

// foo_ut.js

const jQuery = require("../__mocks__/jquery").jQuery;

describe("Description", () =>
  test("Test", () => {
    jQuery.get.mockClear();

    // test function that uses $.get() and $(...).find()
  });
});

Solution 2:[2]

I use JSDom to inject the JQuery package into my window.

var jsdom = require("jsdom");
    const dom = new JSDOM(`<!DOCTYPE html><p>Hello world</p>`);
var window = dom.window;
var $ = require("jquery")(window);
expect($).to.be.not.undefined;

I also wrote an article about how i did it for vitest, should be similar to jest.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Jon G
Solution 2 alexcodes