Domains not properly catching errors while testing nodeJS in mocha

前端 未结 2 854
暗喜
暗喜 2021-01-01 05:21

When running tests that utilize domains for error handling, Mocha still appears to be throwing an error even if a domain handler inside a library should have caught the erro

相关标签:
2条回答
  • 2021-01-01 05:45

    nodejs domains do definitely catch synchronous errors

    See this simple test case

    var domain = require("domain");
    var d = domain.create();
    
    d.on("error", function() {
        console.log("domain caught");
    });
    
    
    d.run(function() {
        throw new Error("foo");
    });
    
    
    // result: domain caught
    

    EDIT: Since writing this answer I have written a blog post describing what is going on with domains and try catch and whether you can use domains as a wholesale replacement for try catch. It summarises most of what has been discussed here.

    http://www.lighthouselogic.com/node-domains-as-a-replacement-for-try-catch/

    ORIGINAL ANSWER:

    Actually there is a problem with Mocha.

    I wrote the following test function:

    function error(callback){
         var d = domain.create().on('error', function(err){
            console.log("Caught Error in Domain Handler")
            return callback(err);
        });
        d.enter();
        throw new Error("TestError");
        d.exit();
    }
    

    Then I wrote a simple test without mocha:

    error(function(err){
        if(err)
        {
            console.log("Error was returned");
        }else
        {
            console.log("Error was not returned")
        }
    })
    

    The output I received was:

    Caught Error in Domain Handler
    Error was returned
    

    When I tested using Mocha:

    describe('Domain Tests', function(){
        it('Should return an error when testing', function(done){
            error(function(err){
                if(err)
                {
                    console.log("Error was returned");
                }else
                {
                    console.log("Error was not returned")
                }
                return done();
            })
        });
    });
    

    I received the following output:

    ․
    
      0 passing (4ms)
      1 failing
    
      1) Domain Tests Should return an error when testing:
         Error: TestError
          at error (/Users/bensudbury/Documents/node_projects/testMochaDomains/test.js:9:11)
          at Context.<anonymous> (/Users/bensudbury/Documents/node_projects/testMochaDomains/testMocha.js:6:3)
          at Test.Runnable.run (/usr/local/share/npm/lib/node_modules/mocha/lib/runnable.js:194:15)
          at Runner.runTest (/usr/local/share/npm/lib/node_modules/mocha/lib/runner.js:358:10)
          at /usr/local/share/npm/lib/node_modules/mocha/lib/runner.js:404:12
          at next (/usr/local/share/npm/lib/node_modules/mocha/lib/runner.js:284:14)
          at /usr/local/share/npm/lib/node_modules/mocha/lib/runner.js:293:7
          at next (/usr/local/share/npm/lib/node_modules/mocha/lib/runner.js:237:23)
          at Object._onImmediate (/usr/local/share/npm/lib/node_modules/mocha/lib/runner.js:261:5)
          at processImmediate [as _immediateCallback] (timers.js:330:15)
    

    As you can see: the domain error handler was short circuited.

    This problem seems to be related to the following issues:

    https://github.com/visionmedia/mocha/issues/513

    While the Node issue has been closed, the issue in mocha is still open.

    The workaround that was suggested at: https://gist.github.com/mcollina/4443963 didn't resolve the issue in this case.

    I dug through the code of Mocha and found that the problem occurs because mocha wraps the tests in a try catch block. This means that the exception is caught and never sent to the uncaughtException or _fatalException handler depending upon the version of node that you are using.

    Your workaround is good, but nodejs domains do definitely catch synchronous errors so I wouldn't change your code but instead change your test. Your new test should look like:

    describe("test", function() {
        it("should succeed", function(done) {
            process.nextTick(function(){
                var foo = require("./foo.js");
                foo(function() {
                    console.log("done");
                    done();
                });
            })      
        });
    });
    

    I haven't tested this code, but similar code for my example works properly:

    it('Should return an error when testing', function(done){   
        process.nextTick(function(){
            error(function(err){
                if(err)
                {
                    console.log("Error was returned");
                }else
                {
                    console.log("Error was not returned")
                }
                return done();
            });
        })
    });
    

    I have added a comment to the end of the issue in Mocha to see if it can be resolved:

    https://github.com/visionmedia/mocha/issues/513

    0 讨论(0)
  • 2021-01-01 05:54

    Found the problem. NodeJS domains catch synchronous errors but the event continues to bubble to a try/catch. If you wrap a domain.run() in a try/catch then the domain error handler AND the catch will be executed.

    Thus, it seems the best practice is to use process.nextTick inside all domain.run(). This is shown in the docs example, but isn't expressed as explicitly as I would prefer.

    Example:

    d.run(function() {
        process.nextTick(function() {
            // do stuff
        });
    });
    

    In this case, the flaw is not in Mocha.

    Proof of NodeJS domains not catching synchronous errors in try/catch: https://gist.github.com/owenallenaz/7141699

    0 讨论(0)
提交回复
热议问题