Home >Web Front-end >JS Tutorial >Test that every MutationObserver is disconnected to avoid memory leaks

Test that every MutationObserver is disconnected to avoid memory leaks

WBOY
WBOYOriginal
2024-08-22 22:34:02901browse

Test that every MutationObserver is disconnected to avoid memory leaks

MutationObserver is a useful mechanism to watch for changes in the DOM and respond to them.

The MutationObserver interface provides the ability to watch for changes being made to the DOM tree.

Here is an example that watches for a theme class change.

    function setUpThemeClassObservers() {
        const observer = new MutationObserver(() => {
            const themeClass = this.getThemeClass();
            this.fireStylesChangedEvent('themeChanged');
        });

        observer.observe(this.eGridDiv, {
            attributes: true,
            attributeFilter: ['class'],
        });

        // we must disconnect otherwise "this" will not be GC'd
        // causing a memory leak
        return () => observer.disconnect();
    }

However, if you forget to disconnect you could be exposing yourself to memory leaks depending on what is accessed from within the MutationObserver functions.

Wouldn't it be great to have a test that can validate that we disconnect our observers?

Automatic Validation for Code

It turns out that it is possible to validate that every MutationObserver that is observing the DOM is also disconnected. (You may need more than one test if you have to exercise different code paths in order to setup the MutationObservers)

The idea is to Mock the global MutationObserver with sub mocks for its observe and disconnect methods. Before the mock is returned we record it in an array so that we can validate all instances at the end of the test run.

describe('Mutation Observers Disconnected', () => {

    let originalMutationObserver: typeof MutationObserver;

    const allMockedObservers: any = [];
    const mutationObserverMock = jest.fn<MutationObserver, [MutationCallback]>().mockImplementation(() => {
        const mock = {
            observe: jest.fn(),
            disconnect: jest.fn(),
            takeRecords: jest.fn(),
        };
        allMockedObservers.push(mock);
        return mock;
    });

    beforeEach(() => {
        // Ensure we can restore the real MutationObserver after the test
        originalMutationObserver = global.MutationObserver;
        global.MutationObserver = mutationObserverMock;
    });

    afterEach(() => {
        global.MutationObserver = originalMutationObserver;
    });

    test('observer always disconnected after destroy', async () => {
        const api = createGrid();
        // Perform some actions if required to exercise the code paths
        api.destroy();

        expect(allMockedObservers.length).toBeGreaterThan(0);
        for (const mock of allMockedObservers) {
            expect(mock.observe).toHaveBeenCalled();
            expect(mock.disconnect).toHaveBeenCalled();
        }
    });
});

In this way we can validate that each MutationObserver, setup during the test, is also disconnected at the end of the test.


Stephen Cooper - Senior Developer at AG Grid
Follow me on X @ScooperDev

The above is the detailed content of Test that every MutationObserver is disconnected to avoid memory leaks. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn