Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 Bug: Test stops recognizing exceptions once done has been called #4202

Open
4 tasks done
feckertson opened this issue Mar 15, 2020 · 8 comments
Open
4 tasks done
Labels
area: async related to asynchronous use of Mocha status: accepting prs Mocha can use your help with this one! type: bug a defect, confirmed by a maintainer

Comments

@feckertson
Copy link

Prerequisites

  • Checked that your issue hasn't already been filed by cross-referencing issues with the faq label
  • Checked next-gen ES issues and syntax problems by using the same environment and/or transpiler configuration without Mocha to ensure it isn't just a feature that actually isn't supported in the environment in question or a bug in your code.
  • '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, your usage of Mocha, or Mocha itself
  • Ensured that there is no discrepancy between the locally and globally installed versions of Mocha. You can find them with: node node_modules/.bin/mocha --version(Local) and mocha --version(Global). We recommend that you not install Mocha globally.

Description

Mocha stops recognizing exceptions once done has been called.

Steps to Reproduce

Find a complete example in git, but an outline is given below.

Source

exports.handler = function eventHandler(callback) {
  callback(null, 'oops');
}

Test

const expect = require('chai').expect;
const subject = require('../mocha-done-issue.js');

it('problematic test', (done) => {
  subject.handler((e, d) => {
    done();
    expect(true).to.be.false;
  });
});

Expected behavior: We expect the test to fail because of the failed expect assertion.

Actual behavior: The test passes, but if the two lines are reversed the test fails.

Reproduces how often: 100%

Versions

Multiple including mocha 5.2.0/7.1.0, node 8.10.0/10.16.0 (64-bit) on Windows cmd and Linux bash.

Additional Information

This came to our attention when faulty code similar to the following was submitted for review. Mocha failed to indicate there was a problem with the code even though Stryker reported 100% mutation coverage.

The expect fails during the extraneous call to callback since the statusCode is 200. Consequently no duplicate call to done occurs. If it weren't for the assert failure mocha would catch the duplicate call to done.

exports.handler = function eventHandler(condition, callback) {
    if ( ! condition ) {
        callback(null, {statusCode: 422, body: 'missing required value'});
    }
    callback(null, {statusCode: 200, body: 'OK'});
}
it('test false condition', (done) => {
    subject.handler(false, (e, d) => {
        expect(d.statusCode).to.equal(422);
        done();
      });
});
@craigtaub
Copy link
Contributor

Yes that seems like a bug. Can replicate via

done();
equal(true, false);
done();

It should throw a failure but it passes, not good. Guessing initial done flags as passing which cant be updated or something. Needs more investigating.

@craigtaub craigtaub added type: bug a defect, confirmed by a maintainer area: async related to asynchronous use of Mocha and removed unconfirmed-bug labels Jun 10, 2020
@boneskull
Copy link
Member

@craigtaub Your example is not a problem. writing that JS another way:

done();
throw new Error();
done();

You wouldn't expect the second done() to be called 😄

@feckertson Can you show that your code does not do something like the above? If there's an exception thrown before the second call to done(), there's really nothing we can do about it, since there won't be a second call to done().

Regardless: if Mocha is eating errors, please provide a minimal, reproducible case.

@boneskull boneskull added unconfirmed-bug status: waiting for author waiting on response from OP - more information needed and removed type: bug a defect, confirmed by a maintainer labels Jun 10, 2020
@feckertson
Copy link
Author

feckertson commented Jun 10, 2020

@boneskull
If "assert fails" = "exception thrown", then no, but....

Isn't the point of a unit testing framework to help code authors discover coding mistakes?
In that sense, mocha is not doing the best possible job.

My simple example at the beginning has a an assert after a call to done which is one type of coding mistake, Currently, such a mistake can cause a test to pass for the wrong reason.

I gave the actual coding error which led to this issue under the "Additional Information" heading. One path through that code leads to a duplicate call to the callback function. There is a test that ought to expose that mistake by either failing an assert or by generating a duplicate call to done error, but neither error occurs.

It seems to me like an error should occur if any code is executed by a test after done is called.

@boneskull
Copy link
Member

I misunderstood @craigtaub; I thought he was saying that the code doesn't throw a "multiple calls to done()" exception when it should.

That should throw. This is the circumstance where done is provided, but the code neither returns a Promise nor calls done asynchronously. This code, OTOH, will error as expected:

it('should fail', function(done) {
  setTimeout(() => {
    done();
    throw new Error();
  });
});

I haven't had time to bisect, but it may be a regression introduced by a fix for #4030.

Isn't the point of a unit testing framework to help code authors discover coding mistakes?
In that sense, mocha is not doing the best possible job.

Uh... have you ever written a bug? Please keep the entitlement out of our issue tracker; we don't work for you.

@boneskull boneskull added type: bug a defect, confirmed by a maintainer status: accepting prs Mocha can use your help with this one! and removed area: async related to asynchronous use of Mocha unconfirmed-bug status: waiting for author waiting on response from OP - more information needed labels Jun 10, 2020
@boneskull
Copy link
Member

cc @juergba; the code here was most recently changed when fixing an issue with this.skip()

@juergba
Copy link
Member

juergba commented Jun 11, 2020

Versions
Multiple including mocha 5.2.0/7.1.0, node 8.10.0/10.16.0 (64-bit) on Windows cmd and Linux bash.

#4030 was released in v7.0.0, so probably this is an older issue. I will have a look.

@juergba
Copy link
Member

juergba commented Jun 12, 2020

const assert = require('assert');
it('problematic test', function(done) {
    done();
    console.log('after done');
    assert(false);
});

Above test does not run correctly neither in Mocha v5.2.0, nor v7.2.0, nor in v8.0.1.
It's an async test with explicit call to done, but the function body is running synchronously.

Similar test cases with real async functions are handled by Mocha's uncaughtException listener and work correctly.

@juergba juergba added area: async related to asynchronous use of Mocha and removed status: accepting prs Mocha can use your help with this one! labels Jun 12, 2020
@juergba juergba self-assigned this Jun 12, 2020
@JoshuaKGoldberg JoshuaKGoldberg changed the title mocha stops recognizing exceptions once done has been called. 🐛 Bug: Test stops recognizing exceptions once done has been called Dec 27, 2023
@JoshuaKGoldberg
Copy link
Member

Confirmed that that last code snippet (#4202 (comment)) doesn't fail as expected in Mocha 10.2.0:

const assert = require("assert");

it("problematic test", function (done) {
  done();
  console.log("after done");
  assert(false);
});
$ npm run test

> example-browser@1.0.0 test
> mocha test.spec.js



  ✔ problematic test
after done

  1 passing (5ms)

...while the earlier setTimeout snippet (#4202 (comment)) does fail as expected:

it("should fail", function (done) {
  setTimeout(() => {
    done();
    throw new Error();
  });
});
$ npm run test

> example-browser@1.0.0 test
> mocha test.spec.js



  ✔ should fail
  1) should fail

  1 passing (3ms)
  1 failing

  1) should fail:
     Uncaught 
  Error
      at Timeout._onTimeout (test.spec.js:4:11)
      at listOnTimeout (node:internal/timers:573:17)
      at process.processTimers (node:internal/timers:514:7)

Fun!

@JoshuaKGoldberg JoshuaKGoldberg added the status: accepting prs Mocha can use your help with this one! label Feb 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: async related to asynchronous use of Mocha status: accepting prs Mocha can use your help with this one! type: bug a defect, confirmed by a maintainer
Projects
None yet
Development

No branches or pull requests

5 participants