8000 afterEach called multiple times when there is an error in an inner suite · Issue #3105 · mochajs/mocha · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

afterEach called multiple times when there is an error in an inner suite #3105

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

Closed
4 tasks done
amiram opened this issue Nov 15, 2017 · 1 comment
Closed
4 tasks done

Comments

@amiram
Copy link
amiram commented Nov 15, 2017

Prerequisites

  • Checked that your issue isn't already filed by cross referencing issues with the common mistake 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 avoiding the use of globally installed Mocha.

Description

When you have a nested suite that throws an error, the afterEach in the outer suite may be called twice and/or called in the middle of the inner afterEach.

Steps to Reproduce

  1. Create an outer suite with an inner suite.
  2. Add a test in the inner suite.
  3. Add afterEach hook in both suites.
  4. Add some async calls.
  5. Throw an error after the test ended but before the inner afterEach ended.

Example:

const bluebird = require("bluebird");

describe("outer tests", () => {

  afterEach(async () => {
    console.log("outer afterEach started");
    bluebird.delay(1000);
    console.log("outer afterEach ended");
  });

  function doSomething(cb) {
    fs.exists("temp.sh", () => {
      cb();
      setTimeout(() => {
// This error is thrown after test was ended but before the inner afterEach ended. 
// it will trigger the outer afterEach
        throw new Error(); 
      }, 100);
    });
  }

  describe("inner tests", function () {

    afterEach(async () => {
      console.log("inner afterEach started");
      await bluebird.delay(1000);
      console.log("inner afterEach ended");
    });

    it("should do something", function (done) {
      doSomething(() => {
        done();
      });
    })
  });
});

Output:

outer tests
    inner tests
      ✓ should do something
inner afterEach started
      1) "after each" hook
outer afterEach started
outer afterEach ended


  1 passing (117ms)
  1 failing

  1) outer tests
       inner tests
         "after each" hook:
     Uncaught 
  Error
      at Timeout.setTimeout (src/tests/test.spec.ts:20:15)



inner afterEach ended
  2) "after each" hook

  1 passing (1s)
  2 failing

  1) outer tests
       inner tests
         "after each" hook:
     Uncaught 
  Error
      at Timeout.setTimeout (src/tests/test.spec.ts:20:15)

  2) outer tests
       "after each" hook:
     TypeError: Cannot read property 'call' of undefined

Expected behavior:
Outer afterEach is called once and only after inner AfterEach ended.

Actual behavior: [What actually happens]
Outer AfterEach is called before inner AfterEach ended. If you play with the delays you might see that sometimes the outer AfterEach is called twice, but it is hard to reproduce.

Reproduces how often:
Always

Versions

mocha - 4.0.1
node - 6.12.0
OS - ubuntu 17.10 x64
Shell - bash
typescript 2.3.4

Additional Information

@ScottFreeCode
Copy link
Contributor

I think what is happening here is much simpler than the example makes it look...

  1. The test tells Mocha's it's passed (done()), but has left an asynchronous action running that will later fail. Note that this would be the same if the test was completely synchronous (no promise or done callback) and started the same asynchronous throwing action.
  2. That action fails during the asynchronous afterEach hook, and it's a known limitation that Mocha interprets asynchronous failures as belonging to the current asynchronous test or hook: timeouts and extant callbacks, forced failures and lying stack traces #967
  3. Since the afterEach's asynchronous code is not what actually failed, it is still running -- effectively a fractal pattern of the same issue: Mocha doesn't intercept asynchronous actions, doesn't track which are from which tests/hooks and if you tell Mocha that something is passed/failed (e.g. by throwing an asynchronous exception in step 2) then it will not stop the other asynchronous work that was started by that test or hook and is still running.
  4. Because the hook completes via promise after (as far as Mocha can tell) failing via asynchronous exception (from the test, per step 2), Mocha is told the hook completes twice, which triggers duplicate end-summary output as in Duplicated failing test summary #2906 (with an additional error reported about the double-completion of the hook)

Closing this as a duplicate of #967 and duplicate of #2906, but feel free to weigh in on either or both of those issues!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants
0