Search code examples
node.jsmocha.jswebapits-mockito

Mock WebAPI interface using ts-mockito


I'm writing a unit test for a class which uses browser WebAPI interface.
I use ts-mockito to mock the interface (a WebGL2RenderingContext in my case).

When I run the test, Node throws ReferenceError: WebGL2RenderingContext is not defined which is understandable, because the test is run under NodeJS environment, not browser, so the class/interface doesn't exist.

Is there any way to make NodeJS environment aware of the WebAPI interfaces, so that it's possible to be mocked?

NOTE: Since it's a unit test, it should NOT be run on a real browser.
jsdom seems to be a possible solution, but I have no idea how to mock it with ts-mockito.


The following snippet illustrate what I'm trying to do:

import { mock, instance, verify } from 'ts-mockito'

// ========== CLASS ==========
class DummyClass {
    dummyMethod() : void {}
}

class TestedClass {
    static testDummy(dummy : DummyClass) : void {
        dummy.dummyMethod();
    }

    static testGlCtx(glCtx : WebGL2RenderingContext) : void {
        glCtx.flush();
    }
}

// ========== TEST ==========
describe('DummyClass', () => {
    // This test passed successfully
    it('works fine', () => {
        const mockDummy = mock(DummyClass);

        TestedClass.testDummy( instance(mockDummy) );

        verify( mockDummy.dummyMethod() ).once();
    });
});

describe('WebGL interface', () => {
    it('works fine', () => {
        // This line failed with 'ReferenceError: WebGL2RenderingContext is not defined'
        const mockGLCtx = mock(WebGL2RenderingContext);

        TestedClass.testGlCtx( instance(mockGLCtx) );

        verify( mockGLCtx.flush() ).once();
    });
});

Run using mocha with the command mocha --require ts-node/register 'test.ts'.


Solution

  • There are two solutions: For common DOM APIs, and for generic mocking.

    For common DOM APIs

    As detailed in this StackOverflow answer, jsdom can be used to bring DOM APIs into NodeJS runtime environment.

    Run npm install --save-dev jsdom global-jsdom
    and change Mocha's command to

    mocha --require ts-node/register --require global-jsdom/register 'test.ts'
    

    NOTE: global-jsdom is the newer & updated version of jsdom-global.

    This solution works for common DOM APIs (such as HTMLElement, SVGElement, File),
    but it doesn't work for more specialized APIs (WebGL, Crypto, audio & video).

    For generic interface mocking

    Turns out ts-mockito has a way to mock interfaces, including DOM & any browser Web APIs.

    So the above test code can be changed to:

    describe('WebGL interface', () => {
        it('works fine', () => {
            const mockGLCtx = mock<WebGL2RenderingContext>();
    
            TestedClass.testGlCtx( instance(mockGLCtx) );
    
            verify( mockGLCtx.flush() ).once();
        });
    });
    

    and the test will run successfully.