Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I prevent Mocha from trapping unhandled exceptions?

I am writing a test suite for a library that performs handling of exceptions that are not handled by a catch statement. The library listens to error events that trickle up to the global scope (e.g. window.addEventListener("error", ...)).

However, if I want to test whether it is really able to detect unhandled exceptions, I can't because Mocha treats such exceptions as test failures. And I cannot use assertions like expect(foo).to.throw, because if I use these, then the exception is caught by expect and is no longer an unhandled exception: it won't trigger the global listener that my library installs and that I want to test.

I've tried allowUncaught but that did not solve the problem. Here is an example test that reproduces the problem:

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/xhtml; charset=utf-8"/>
    <link href="node_modules/mocha/mocha.css" type="text/css" media="screen" rel="stylesheet" />
    <script src="node_modules/mocha/mocha.js"></script>
  </head>
  <body>
    <div id="mocha"></div>
    <script>
      mocha.setup('bdd');
      mocha.allowUncaught();

      it("should be fine", function (done) {
        // Simulate an error thrown by asynchronous code.
        setTimeout(function () {
          throw new Error("error");
        }, 100);

        // My actual error handler is bigger than this. This is just to
        // simulate what my actual test suite does.
        function listener(ev) {
          // Uninstall ourselves.
          window.removeEventListener("error", listener);
          done(); // We detected the error: everything is fine.
        }
        window.addEventListener("error", listener);
      });

      mocha.run();
    </script>
  </body>
</html>

The test above should pass. However, when I run it Mocha reports one failure and one success! How can I get Mocha to ignore the uncaught exception and let my custom code handle it?

like image 615
Louis Avatar asked Nov 09 '22 09:11

Louis


1 Answers

Mocha installs its own unhandled exception event listener on window and allowUncaught does not prevent it. What you need to do is uninstall this handler with:

Mocha.process.removeListener("uncaughtException");

Here is an example that temporarily turns off Mocha's unhandled exception handling:

  mocha.setup('bdd');
  mocha.allowUncaught();

  var savedHandler = undefined;
  before(function () {
    // Save Mocha's handler.
    savedHandler = window.onerror;
  });

  describe("without handler", function () {
    before(function () {
      // Stop Mocha from handling uncaughtExceptions.
      Mocha.process.removeListener("uncaughtException");
    });

    it("should be fine", function (done) {
      setTimeout(function () {
        throw new Error("error");
      }, 100);

      function listener(ev) {
        window.removeEventListener("error", listener);
        done();
      }
      window.addEventListener("error", listener);
    });

    after(function () {
      // Restore the handler so that the next tests are treated as
      // mocha treats them.
      window.onerror = savedHandler;
    });
  });

  describe("with handler", function () {

    it("should fail", function (done) {
      setTimeout(function () {
        throw new Error("error");
      }, 100);
    });
  });

  mocha.run();

The first test will pass and will be counted only once. The 2nd test will fail, as we expect because Mocha's unhandled exception handler is in effect.

The reason you get one pass and one failure even though you have only one test is a pecularity of Mocha. It detects the unhandled exception and so it declares that the test has failed. But then your code calls done so Mocha declares the test has passed and counts it twice.

Note that the method used above is not documented and could break with future releases of Mocha. As far as I can tell, there is no "official" method to get the desired results.

like image 159
Louis Avatar answered Nov 14 '22 23:11

Louis