Skip to content

[Feature]: Exclude ɵfac ɵprov properties from auto mock #2908

Open
@MillerSvt

Description

@MillerSvt

🚀 Feature Proposal

Exclude ɵfac ɵprov properties from jest auto mock

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.

This happens because of jest auto mocks, mocking ɵfac and ɵprov as well. Is it possible to exclude them?

Example

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. Perhaps it could be placed there?

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions