Mocha test executes callback twice on failure

2019-08-03 09:18发布

问题:

I have a simple Mongoose schema that I'm testing with Mocha; when I run the test with 'success' callback it executes normally, however when the last test executes it fails and appears to run the test again (I get two conclusions, one which populates the error object and the second one which returns null in the error object.) Running the two tests below results in the following output:

Cabinet: 
  â should return all authorized  
  â should return not authorized  <-- it succeeds the first time?
  1) should return not authorized 


2 passing (42ms)  
1 failing         <--- what?  there are only two tests

1) Cabinet:  should return not authorized :
   Uncaught AssertionError: expected null to exist  <--- see test

THIS TEST REPEATS

it("should return not authorized error ", function(done){
    var newCabinet = {
        name:  "A new cabinet",
        description: "Some remote cabinet",
        authorizedBorrowers : ["imatest","imanothertest"],
        cabinetInventory : []
    };
    Cabinet.newCabinet(newCabinet, function(err, cabinet){
        if (err) {
            console.log("Unable to create cabinet");
            done(err);
        }
        Cabinet.isAuthorizedBorrower(cabinet._id, "bob", function(cberr, borrower){
            should.exist(cberr);  <-- 'expected null to exist' points here
            done();
        });
    });
});

THIS TEST WORKS

it("should not return unauthorized error ", function(done){
    var newCabinet = {
        name:  "A new cabinet",
        description: "Some remote cabinet",
        authorizedBorrowers : ["imatest","imanothertest"],
        cabinetInventory : []
    };
    Cabinet.newCabinet(newCabinet, function(err, cabinet){
        if (err) {
            console.log("Unable to create cabinet");
            done(err);
        }
        //console.log("ID " + cabinet._id)
        Cabinet.isAuthorizedBorrower(cabinet._id, "imatest", function(cberr, borrower){
            should.not.exist(cberr);
            done();
        });
    });
});

The schema

var cabinetSchema = new Schema({
  name:  String,
  description: String,
  thumbnail : Buffer,
  authorizedBorrowers : [],
  defaultCheckout : {type : Number, default: 0} // default checkout mins = no time

});

var hasBorrower = function(cabinet, borrower){
  if (cabinet===null) return false;
  if (cabinet.authorizedBorrowers.length === 0) return false;
  return (cabinet.authorizedBorrowers.indexOf(borrower) >= 0)
}  

cabinetSchema.statics.isAuthorizedBorrower = function(cabinet_id, borrowername, cb ){
  this.findOne({_id: cabinet_id}, function(err, cabinet){
    if (err) cb(err,null);
    if (!hasBorrower(cabinet, borrowername)) cb(new Error(errorMsgs.borrowerNotAuthorized),null);
    cb(null,borrowername);
  });
};

回答1:

Whenever you do this, add a return; to avoid calling the done callback twice. This is for mocha but also for general node.js callback handling.

    if (err) {
        console.log("Unable to create cabinet");
        done(err);
        return;
    }

Same problem in your Cabinet schema:

if (err) cb(err,null);

That needs a return or it will invoke the callback twice and cause chaos (also affectionately known amongst the node.js blogosphere as one flavor of "releasing Zalgo").