Mocking functions

Contents

This chapter covers mocking functions and Enonic libraries.

Source code

To demonstrate the power of mocking, let’s make a simple Typescript using a standard Enonic library:

src/main/resources/lib/myproject/sanitize.ts
export {sanitize} from '/lib/xp/common';

Test without mock

If you were to run this simple test:

import {
    describe,
    expect,
    test as it
} from '@jest/globals';
import { sanitize } from '/lib/myproject/sanitize';


describe('sanitize', () => {
    it('should return the first 10 numbers in the fibonacci sequence', () => {
        expect(sanitize("Piña CØLADÆ <script>alert('hi!');</script>"))
            .toEqual('pina-coladae-script-alerthi-script');
    });
});

It would fail with the following error message:

Cannot find module '/lib/xp/common' from 'src/main/resources/lib/myproject/sanitize.ts'

This happens because the module file /lib/xp/common isn’t available in the test environment. It only exists inside the compiled Enonic application file, which is used at runtime in the Enonic XP server.

In order to run the test successfully, a "fake" version of the module must be made available in the test environment. This is where mocking comes in handy.

Adding the mock

Add the following test to your project:

src/jest/server/sanitize.test.ts
import type { sanitize as sanitizeType } from '@enonic-types/lib-common';


import {
    describe,
    expect,
    jest,
    test as it
} from '@jest/globals';


jest.mock('/lib/xp/common', () => ({
    sanitize: jest.fn<typeof sanitizeType>().mockImplementation((text) => text
        .toLowerCase()
        .replace('ñ', 'n')
        .replace('ø', 'o')
        .replace('æ', 'ae')
        .replace(/[\(\)']+/, '')
        .replace(/[^a-z0-9-]+/g, '-')
        .replace(/^-|-$/, '')
    )
}), { virtual: true });


describe('sanitize', () => {
    it('should remove, replace or fold "illegal" characters', () => {
        import('/lib/myproject/sanitize').then(({sanitize}) => {
            expect(sanitize("Piña CØLADÆ <script>alert('hi!');</script>"))
                .toEqual('pina-coladae-script-alerthi-script');
        });
    });
});

Here, sanitize is no longer "statically" imported at the beginning of the file. This is because the mock must be set up before the import is done. That’s why a dynamic import in each test is used instead.

Obviously this mock doesn’t cover ascii-folding for the whole unicode range, but is sufficient enough to run this test.

Summary

This chapter showed how to mock an Enonic library invoked by a Typescript. This is necessary when you want to test your code in a controlled environment, without having to rely on the actual library being present.

Writing mocks for entire libraries can be a bit tedious, so in the next chapter we will rather use the Mock XP library.


Contents

Contents