Testing MutationObserver with Jest

I know I'm late to the party here, but in my jest setup file, I simply added the following mock MutationObserver class.

global.MutationObserver = class {
    constructor(callback) {}
    disconnect() {}
    observe(element, initObject) {}
};

This obviously won't allow you to test that the observer does what you want, but will allow the rest of your code's tests to run which is the path to a working solution.


The problem is actually appears because of JSDom doesn't support MutationObserver, so you have to provide an appropriate polyfill.

Little tricky thought may not the best solution (let's use library intend for compatibility with IE9-10).

  • you can take opensource project like this one https://github.com/webmodules/mutation-observer which represents similar logic
  • import to your test file and make global

Step 1 (install this library to devDependencies)

npm install --save-dev mutation-observer

Step 2 (Import and make global)

import MutationObserver from 'mutation-observer'
global.MutationObserver = MutationObserver 

test('your test case', () => { 
   ...
})

You can use mutationobserver-shim.

Add this in setup.js

import "mutationobserver-shim"

and install

npm i -D mutationobserver-shim

I think a fair portion of the solution is just a mindset shift. Unit tests shouldn't determine whether MutationObserver is working properly. Assume that it is, and mock the pieces of it that your code leverages.

Simply extract your callback function so it can be tested independently; then, mock MutationObserver (as in samuraiseoul's answer) to prevent errors. Pass a mocked MutationRecord list to your callback and test that the outcome is expected.

That said, using Jest mock functions to mock MutationObserver and its observe() and disconnect() methods would at least allow you to check the number of MutationObserver instances that have been created and whether the methods have been called at expected times.

const mutationObserverMock = jest.fn(function MutationObserver(callback) {
    this.observe = jest.fn();
    this.disconnect = jest.fn();
    // Optionally add a trigger() method to manually trigger a change
    this.trigger = (mockedMutationsList) => {
        callback(mockedMutationsList, this);
    };
});
global.MutationObserver = mutationObserverMock;

it('your test case', () => {
    // after new MutationObserver() is called in your code
    expect(mutationObserverMock.mock.instances).toBe(1);

    const [observerInstance] = mutationObserverMock.mock.instances;
    expect(observerInstance.observe).toHaveBeenCalledTimes(1);
});


Since it's not mentioned here: jsdom has supported MutationObserver for a while now.

Here's the PR implementing it https://github.com/jsdom/jsdom/pull/2398