Skip to content Skip to sidebar Skip to footer

How To Mock Jquery .done() So It Executes Correctly With Jest?

I'm trying to write a unit-test for a password changing React-module, but i can't get the code to be executed in brackets. I've written a mock for the module MyAPI, the mock code i

Solution 1:

You want that .done or .error methods are executed but don't want to actually make a request (btw. i don't know about an .error method just about .fail) ? Then i would do the following:

Mock jQuery globally

Create a global mock for jquery inside a __mocks__ directory at the top level of your working directory:

//__mocks__/jquery.js:const jQ = jest.requireActual("jquery");

const ajax = jest.fn(() => {
    return jQ.Deferred();
});

exportconst $ = {
    ...jQ,  // We don't want to mock jQuery completely (we might want to alter $.Deferred status)
    ajax,
};

exportdefault $;

By putting jquery.js inside the __mocks__ directory jQuery gets automatically mocked by jest when requested in modules you want to test (well, in this case it gets partially mocked...).

With this setup you can just run your code without making an actual request but normally run .done and .error methods and the registered callbacks.

Mock .done and .fail methods

If you don't want to execute the registered callbacks in .done or .fail you need to mock them by hand and instead of returning jQ.Deferred() return a plain javascript object with jest mocks.

Inside a specific test case where you definitly don't want that .done/.error calls your registered callback:

// By returning "this" we are able to chain in the way $.ajax("/api", params).done().fail()const jqXHR = {
    done: jest.fn().mockImplementation(function () {
        returnthis;
    }),
    fail: jest.fn().mockImplementation(function () {
        returnthis;
    }),
    // some more $.Deferred() methods you want to mock
};

// Overwrite the global $.ajax mock implementation from __mocks__/jquery.js with our custom one
$.ajax.mockImplementation(() => jqXHR)

Simulate success or error

When you want to simulate success or error inside a specific test case again overwrite the global mock implementation:

For success:

// successconst dfd = $.Deferred();
$.ajax.mockImplementation(() => {
    return dfd.resolve("success"); // this is what your done callback will receive as argument
});

For error:

// successconst dfd = $.Deferred();
$.ajax.mockImplementation(() => {
    return dfd.reject("error"); // this is what your fail callback will receive as argument
});

Note that it does not make sense to assert that .done or .fail was called/not called since both are always called because they register the callbacks you put inside them. Only when $.Deferred resolves or rejects a specific registered callback gets executed, which you then can test.

For better testability w.r.t unit testing you should factor out the anonymous functions from .done/.error. Since JavaScript is weird and not like python (which i like more) you cannot easily mock specific functions inside a module under test. So you would need to put them inside a dedicated module and mock this module completely. Then you could just assert that they were called either in success or error case.

It took me a while to figure out how to correctly handle mocking with jquery so i want to share my experience here. Hope this helps...

Post a Comment for "How To Mock Jquery .done() So It Executes Correctly With Jest?"