jest icon indicating copy to clipboard operation
jest copied to clipboard

resetAllMocks between describe blocks is counter-intuitive

Open bogdanb opened this issue 4 years ago • 4 comments

🐛 Bug Report

When you run jest.resetAllMocks() in one describe block, it will also reset mocks defined in other describe blocks. This struck me as counter-intuitive, even if after reading the docs carefully it seems to be how it is documented.

To Reproduce

Steps to reproduce the behavior: Run the following test file:

const topLevel = jest.fn();

describe("first block", () => {
  const one = jest.fn();
  const two = jest.fn();
  const three = jest.fn();

  afterEach(() => jest.resetAllMocks());

  // Assume the tests in this block each configure the mocks with some values / implementation
  test("first block test", () => {
    expect([topLevel, one, two, three]).toBeDefined();
  });
});

describe("second block", () => {
  const local = jest.fn(() => 123);

  test("second block test", () => {
    expect(local()).toBe(123); // Fails! Expected: 123 / Received: undefined
  });
});

Expected behavior

The intuitive behavior, IMO, would be for the second block test to succeed. One expects that what happens in one describe block does not affect other describe blocks, except via global state.

I think I understand what actually happens: Jest first runs both describe blocks, and it gathers tests and before/after blocks. Then it starts running each test (with any appropriate before/after blocks). This means that the local mock is defined (and captured by the second block tests) before any test is run. Then Jest runs the first test; its after block calls resetAllMocks, which also resets the local mock, removing its implementation. Then Jest runs the second test, but local no longer has an implementation, so it fails.

I think the intuitive behavior would be that resetAllMocks (and other similar methods such as clearAllMocks), when called from inside a describe block, would only affect (1) mocks defined in that describe block and (2) mocks defined in parent describe blocks and/or at global scope.

(I mean, probably the correct thing would be to cleared/reset mocks after every test by default, or even forcefully, but that I understand might take some time.)

Also, if mocks are cleared/reset after each test by default, jest.fn, spy and similar functions should not accept an implementation or other mock configuration when called directly inside a describe block. Mocking values and implementations should only be allowed inside tests and before/after blocks, anything else is a clear error, since they would be lost after each test.

Link to repl or repo (highly encouraged)

https://repl.it/@BogdanButnaru/jest-playground

envinfo

npx: installed 1 in 2.3s

  System:
    OS: Windows 10 10.0.18363
    CPU: (4) x64 Intel(R) Core(TM) i5-7300U CPU @ 2.60GHz
  Binaries:
    Node: 14.7.0 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.4 - ~\AppData\Roaming\npm\yarn.CMD
    npm: 6.14.11 - C:\Program Files\nodejs\npm.CMD

bogdanb avatar Feb 08 '21 12:02 bogdanb