Skip to content

Jest/ESBuild mocking JS modules #25582

@gultyayev

Description

@gultyayev

Command

test

Is this a regression?

  • Yes, this behavior used to work in the previous version

The previous version in which this bug was not present was

No response

Description

With Jest Angular started to use ES Build internally. This causes ES Modules in the output with non-configurable methods/properties. As a result, we cannot mock JS modules. This leads to tests that pull lots of external dependencies (utility functions, config objects etc.) that are not provided using the DI and hence cannot be stubbed using Angular DI.

Minimal Reproduction

Scaffold an app and use Jest as a testing tool. Modify AppComponent.ngOnInit

ngOnInit() {
  if (isDevMode())  {
    console.log('dev');
  }
}

In the test try to stub the isDevMode.

jest.mock('@angular/core', () => ({
    ...jest.requireActual('@angular/core'),
    isDevMode: jest.fn().mockReturnValue(false)
}))

describe('AppComponent', () => {
    let component: AppComponent;

    beforeEach(() => {
        component = new AppComponent()
    })
    
    describe('ngOnInit', () => {
        it('should not log when in prod mode', () => {
            const spy = jest.spyOn(console, 'log')
            component.ngOnInit()
            expect(spy).toHaveBeenCalled()
        });
    })
})

Exception or Error

Test fail due to a number of reasons which are related to ES Build in the end:
a. ES Build outputs a module with a different name and there is a mismatch
b. ES Build embeds a module (e.g. import of the environments file)
c. JS runtime error that you try to change the property that cannot be configurable. This happens if you try to import the module in the tests and spy on it.


import * as module from 'module'

jest.spyOnProperty(module, 'method').mockReturnValue(...)


### Your Environment

```text
Angular CLI: 16.0.5
Node: 18.16.1
Package Manager: npm 9.5.1
OS: darwin arm64

Angular: 
... 

Package                      Version
------------------------------------------------------
@angular-devkit/architect    0.1600.5 (cli-only)
@angular-devkit/core         16.0.5 (cli-only)
@angular-devkit/schematics   16.0.5 (cli-only)
@schematics/angular          16.0.5 (cli-only)

Anything else relevant?

This feels like a big deal, because most of the current state tests will become invalid. Furthermore, current behavior makes unit tests more of the integration sorts.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions