mocha icon indicating copy to clipboard operation
mocha copied to clipboard

🐛 Bug: Watch + mochaHooks inconsistent state on re-runs

Open Kartones opened this issue 1 year ago • 4 comments

Bug Report Checklist

  • [X] I have read and agree to Mocha's Code of Conduct and Contributing Guidelines
  • [X] I have searched for related issues and issues with the faq label, but none matched my issue.
  • [X] I have 'smoke tested' the code to be tested by running it outside the real test suite to get a better sense of whether the problem is in the code under test, my usage of Mocha, or Mocha itself.
  • [x] I want to provide a PR to resolve this

Expected

Mocha's watch mode should produce the same results on all runs, if the code does not change significantly (e.g. triggered by adding a new line.). Using mochaHooks that mutate globals and modules/requires should be consistent in all runs.

  Failing on watch reload
mochaHooks beforeEach(): [ 'testItem1', 'testItem2' ]
    ✔ should return two items

  Always working
mochaHooks beforeEach(): [ 'testItem1', 'testItem2' ]
    ✔ should return two items

  2 passing (2ms)

Actual

Mocha's watch mode seems to not work the same on reloads regarding mochaHooks as in the 1st run.

Having a beforeEach() that requires a fil, and which we mutate state of in that hook, when a test that relies on that mutated state runs, first time is ok, but subsequent "watch runs" fail.

First run is ok, but then any irrelevant change on a watched file:

  Failing on watch reload
mochaHooks beforeEach(): [ 'testItem1', 'testItem2' ]
    1) should return two items

  Always working
mochaHooks beforeEach(): [ 'testItem1', 'testItem2' ]
    ✔ should return two items

  1 passing (13ms)
  1 failing

  1) Failing on watch reload
       should return two items:

      AssertionError [ERR_ASSERTION]: Expected values to be loosely deep-equal:
[]

should loosely deep-equal
[
  'testItem1',
  'testItem2'
]
      + expected - actual

      -[]
      +[
      +  "testItem1"
      +  "testItem2"
      +]

      at Context.<anonymous> (test/failing.test.js:8:12)
      at process.processImmediate (node:internal/timers:478:21)


ℹ [mocha] waiting for changes...

Minimal, Reproducible Example

I created a small repro repo, keeping it similar to my source: https://github.com/Kartones/mocha-hooks-watcher-bug

Includes a README with steps to reproduce, but adding here a minimal TL;DR:

given:

class A {
  _items = [];

  initialize(items) {
    this._items = items;
  }

  getItems() {
    return this._items;
  }
}

exports.A = A;
exports.a = new A();

And a hook that mutates it:

const { a } = require("../lib/a.js");

exports.mochaHooks = {
  beforeEach() {
    a.initialize(["testItem1", "testItem2"]);
  },
};

The hook is correctly executed before each test, but a seems to be kept at the initial state (with _items=[], instead of _items=["testItem1", "testItem2"])

Versions

Mocha: 10.4.0 NodeJS: v20.11.1 pnpm (not strictly relevant): [email protected]

Additional Info

UPDATED: Taking a look, will provide a PR with one solution.

Kartones avatar May 28 '24 08:05 Kartones

Fantastic investigation, thanks! I've gone through the (very helpful!) reproduction and confirmed the bug. +1 that only clearing some of the file cache is a root cause bug.

Unfortunately, I think that fixing the behavior by clearing more of the cache would be considered a semver-major breaking change. It's likely that some users have come to depend on this odd caching behavior.

cc @mochajs/maintenance-crew for a second or third opinion, just to be safe.

JoshuaKGoldberg avatar Jul 03 '24 15:07 JoshuaKGoldberg

Thanks for checking it out!

Bug fixed, now doing a full blast, and no problem in considering it a major due to potential breakages 👍

Kartones avatar Jul 07 '24 21:07 Kartones

Thanks for the investigation. We are also running into this problem. This has helped us as a workaround. Thank you so much!

kirrg001 avatar Nov 11 '24 12:11 kirrg001

I couldn't get @kirrg001 's workaround to work for me, so I just wanted to call out @Kartones 's workaround in case anyone stumbles upon this issue.

In the meantime, the quick solution to this cache issue, for those facing it, is to run in parallel-watch mode (--parallel --watch). It is a bit slower, but works fine.

timorthi avatar May 12 '25 18:05 timorthi