Mocking
Contents
This chapter introduces the concept of mocking
. It shows how to mock functions and Enonic libraries.
Introduction
Mocking is the concept of creating fake functions or objects that stand in for real functions or objects in the system. This way, you may test or simulate parts of a system - even without that system being actually available.
Source code
To demonstrate the power of mocking, let’s make a simple Typescript using a standard Enonic library:
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:
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.