jest icon indicating copy to clipboard operation
jest copied to clipboard

[Bug]: resetAllMocks restoring mocks instead of resetting them

Open vetruvet opened this issue 2 years ago • 9 comments
trafficstars

Version

29.4.0

Steps to reproduce

Simple test to reproduce:

test('it should reset properly', () => {
  const obj = {
    foo: () => 42,
  };

  const fooSpy = jest.spyOn(obj, 'foo');

  console.log(obj.foo());
  expect(obj.foo).toHaveBeenCalled();

  jest.resetAllMocks();

  console.log(obj.foo());
  expect(obj.foo).toHaveBeenCalled();
});

Expected behavior

The test should pass

Actual behavior

The test fails with Matcher error: received value must be a mock or spy function.

Additional context

It seems that resetAllMocks is acting like mockRestore instead of mockReset.

If I replace jest.resetAllMocks() with fooSpy.mockReset(), the test passes as expected, whereas if I replace it with fooSpy.mockRestore() it fails with that error again.

Environment

System:
    OS: Linux 5.15 KDE neon 5.26 5.26
    CPU: (16) x64 AMD Ryzen 7 PRO 6850U with Radeon Graphics
  Binaries:
    Node: 18.12.0 - ~/.nvm/versions/node/v18.12.0/bin/node
    npm: 8.19.2 - ~/.nvm/versions/node/v18.12.0/bin/npm
  npmPackages:
    jest: ^29.4.0 => 29.4.0

vetruvet avatar Jan 24 '23 17:01 vetruvet

Are you working on this?

pratik9333 avatar Jan 24 '23 19:01 pratik9333

No, I don't know enough about Jest internals to suggest a fix.

~Besides, I'd expect that a certain multi-billion-dollar corporation would be maintaining this. Don't get me wrong, I'm all for contributing to open source but if the project is backed by a huge corporation, I think they can spare the time to fix it.~ (see https://jestjs.io/blog/2022/05/11/jest-joins-openjs, thanks @mrazauskas for pointing it out)

vetruvet avatar Jan 24 '23 20:01 vetruvet

This line in this PR introduced the regression; https://github.com/facebook/jest/pull/13692/files#diff-7ae0bd704c3c2789b19abe2bbf94aca3505e2a6b3823d85c7f6b316b216d37c9R1411

Looking here (https://github.com/facebook/jest/pull/13692/files#diff-7ae0bd704c3c2789b19abe2bbf94aca3505e2a6b3823d85c7f6b316b216d37c9L1409), the same function is called to restore the mocks. While it's called reset in the lambda function of the new addition, it's still the same function being called.

Unsure if just removing this call is the solution, or if something else needs to be done to still retain functionality of that PR

I'm actually a bit confused, as it seems the point of that PR (and the issue it fixes) is to make the behaviour of mockReset similar to mockRestore? AFAICT it's still a regression on resetAllMocks though, as that's actually removing the mock status of the function. But the original purpose of that PR is still a breaking change either way

me4502 avatar Jan 25 '23 03:01 me4502

@feliperli Could you take a look, please? Seems like your PR (#13692) introduced this issue. Or is that expected behaviour?

mrazauskas avatar Jan 25 '23 09:01 mrazauskas

Besides, I'd expect that a certain multi-billion-dollar corporation would be maintaining this.

By the way, Jest is just another open source project maintained by few volunteers. See https://jestjs.io/blog/2022/05/11/jest-joins-openjs

mrazauskas avatar Jan 25 '23 09:01 mrazauskas

TIL, thanks for pointing that out.

I did end up doing some digging, and while I'm not any closer to finding the root cause, I did notice this:

  resetAllMocks(): void {
    this._spyState.forEach(reset => reset());
    this._mockConfigRegistry = new WeakMap();
    this._mockState = new WeakMap();
  }

  restoreAllMocks(): void {
    this._spyState.forEach(restore => restore());
    this._spyState = new Set();
  }

It seems that both of those methods end up calling all the _spyState functions and on line 1401 just above, you see this._spyState.add(restore); so it seems that resetAllMocks is calling the restore function (but confusingly naming it reset in the forEach).

I'm not sure if this is intentional, or if it's not, what a fix would look like, but hopefully this info at least helps.

vetruvet avatar Jan 25 '23 15:01 vetruvet

I'm not sure if this is intentional, or if it's not, what a fix would look like, but hopefully this info at least helps.

also seeing this impact our unit tests, with only a regenerated lockfile causing the breaking change

bmuenzenmeyer avatar Jan 26 '23 21:01 bmuenzenmeyer

@feliperli Could you take a look, please? Seems like your PR (#13692) introduced this issue. Or is that expected behaviour?

Oh no :( I'm looking into it.

feliperli avatar Jan 26 '23 21:01 feliperli

I also see this!!!!

ghost avatar Jan 27 '23 08:01 ghost

I have a different problem, but I think the cause is the same change in mockReset. Someone called spyOn on a mock function and it caused a maximum call stack error This is an example that reproduces the error:

describe('test console.log', () => {
  console.log = jest.fn();
  const log = jest.spyOn(console, 'log');

  afterEach(() => {
    log.mockReset();
  });

  test('console.log a', () => {
    console.log('test a');

    expect(log).toBeCalledWith('test a');
  });

  test('console.log b', () => {
    /**
     * An error is thrown on the next line:
     * RangeError: Maximum call stack size exceeded
     * at WeakMap.get (<anonymous>)
     * at ModuleMocker._ensureMockState (node_modules/jest-mock/build/index.js:275:33)
     */
    console.log('test b');

    expect(log).toBeCalledWith('test b');
  });
});

When I remove the line this._mockConfigRegistry.set(f, originalMockImpl); it works fine.

EladHeller avatar Jan 29 '23 07:01 EladHeller

Seems related: I'm also getting random RangeError: Maximum Call Stack Size Exceeded that are pointing to the jest-mock package, reverting pack to 29.3.1 fixes the issue.

rdsedmundo avatar Jan 31 '23 21:01 rdsedmundo

Should be fixed in #13866

mrazauskas avatar Feb 08 '23 18:02 mrazauskas

https://github.com/facebook/jest/releases/tag/v29.4.3

SimenB avatar Feb 15 '23 12:02 SimenB

Looks like the bug wasn't fixed in mockReset

I've created a new issue at https://github.com/facebook/jest/issues/13916

trivikr avatar Feb 15 '23 16:02 trivikr

29.4.3 fixes the original issue for me, along with the "Maximum Call Stack Size Exceeded" error. Thanks all!

vetruvet avatar Feb 15 '23 19:02 vetruvet

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

github-actions[bot] avatar Mar 18 '23 00:03 github-actions[bot]