Description
🚀 Feature Proposal
I suggest adding the ability to interfere with the jest auto mock logic in order to be able to modify its behavior at the presets level.
The original problem is that when you try to mock the angular library, then when creating the service, undefined is returned. This is because Angular creates services using ɵfac
and ɵprov
, which are replaced with jest.fn()
, which by default return undefined
.
Related to: thymikee/jest-preset-angular#2908
Motivation
Currently, I'm trying to test a component that depends on an external Angular library. This library provides a SomeService
that is provided in root.
Here is my component:
class Component {
someService = inject(SomeService);
method() {
this.someService.someMethod();
}
}
Here is my test:
import { SomeService } from 'some-library';
jest.mock('some-library');
beforeEach(() => {
TestBed.configureTestingModule({
providers: [SomeService],
});
fixture = TestBed.createComponent(Component);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('test', () => {
component.method();
});
Currently I receive error: Cannot read property 'someMethod' of undefined
.
Research
I've done some research. Here is my working patch:
jest.mock(`some-library`, () => {
const moduleMock = jest.createMockFromModule(`some-library`);
function* walk(obj: unknown, walkedNodes: any[] = []): Generator<[key: string, target: any]> {
if ((typeof obj !== `function` && typeof obj !== `object`) || walkedNodes.includes(obj)) {
return;
}
for (const key in obj) {
if (typeof key === `string` && key.startsWith(`ɵ`)) {
yield [key, obj];
}
yield* walk(obj[key], [...walkedNodes, obj]);
}
}
for (const [key, target] of walk(moduleMock)) {
switch (key) {
case `ɵfac`: {
target[key] = () => new target();
break;
}
case `ɵprov`: {
if (target[key] === undefined) {
break;
}
if (`factory` in target[key]) {
target[key].factory = () => new target();
}
break;
}
}
}
return moduleMock;
});
However, it would not be beneficial to repeat it in each test and mock.
Example of proposed API
// setup-jest.ts
jest.onGenerateMock((moduleName, moduleMock) => {
// some modifications for moduleMock
return moduleMock;
});
This hook called only if mock was generated by _generateMock
Pitch
I believe that this can make it easier to work with jest auto mock in projects that often use special tools.