I'm moving this issue from domenic/chai-as-promised#97 - we have a user (@ohjames) who is claiming that while using bluebird, and asserting with chai-as-promised that rejection has been handled, bluebird still believes the rejection has not been handled. To quote the OP:
I would have thought this mocha test would work fine:
// Either I've made a mistake or chai-as-promised is crap... it('finds that chai-as-promised is not broken', function() { var Promise = require('bluebird') var promise = Promise.reject(Error()) return promise.should.be.rejected })It looks like it passes but check out the stderr after the test...
User Model โ finds that chai-as-promised is not broken Unhandled rejection Error at Error (native) at Context.<anonymous> (/home/mega-totoro/projects/cuddle-friends/server/api/user/user.model.spec.js:40:34) at callFn (/home/mega-totoro/projects/cuddle-friends/node_modules/grunt-mocha-test/node_modules/mocha/lib/runnable.js:223:21) at Test.Runnable.run (/home/mega-totoro/projects/cuddle-friends/node_modules/grunt-mocha-test/node_modules/mocha/lib/runnable.js:216:7) at Runner.runTest (/home/mega-totoro/projects/cuddle-friends/node_modules/grunt-mocha-test/node_modules/mocha/lib/runner.js:374:10) at /home/mega-totoro/projects/cuddle-friends/node_modules/grunt-mocha-test/node_modules/mocha/lib/runner.js:452:12 at next (/home/mega-totoro/projects/cuddle-friends/node_modules/grunt-mocha-test/node_modules/mocha/lib/runner.js:299:14) at /home/mega-totoro/projects/cuddle-friends/node_modules/grunt-mocha-test/node_modules/mocha/lib/runner.js:309:7 at next (/home/mega-totoro/projects/cuddle-friends/node_modules/grunt-mocha-test/node_modules/mocha/lib/runner.js:247:23)Or am I doing something stupid?
here is the LOC where chai-as-promised handles the rejection.
Can you fill in the blanks to repro?
var Promise = require("bluebird");
var chai = require("chai");
var chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
var promise = Promise.reject(Error())
promise.should.be.rejected;
Gives:
TypeError: Cannot read property 'be' of undefined
I also cannot find anywhere in chai-as-promise code where it would add .should to promise prototype.
Given @domenic wrote the thing it might be simpler to just ask him :)
This is chai-as-promised not attaching error handlers soon enough, triggering unhandled rejection detection most likely. I'll see if I can PR chai-as-promised.
Meanwhile, you can opt out of unhandled rejection detection for your testing with Primise.onPossiblyUnhandledRejection(function(){}) but honestly this looks like a bug in chai-as-promised that would trigger on every library with unhandled rejection detection turned on (including native promises under certain uses).
It's not their fault as chai-as-promised predates this feature in bluebird and certainly its standardization among promise libraries. Try to be nice to the chai-as-promised authors and maintainers arguments are unnecessary :)
This is chai-as-promised not attaching error handlers soon enough
Thats very unlikely and actually implausible
Yeah, that was just a guess.
@petkaantonov you're missing a chai.should() call by the way.
Also, it looks a-ok http://jsfiddle.net/cg0j7L80/
Your princess is in another castle http://jsfiddle.net/gL4kv7qt/ , this works fine, bug does not reproduce it's possible this was a bug with an old version of bluebird. I'll test node too to make sure.
Update: doesn't reproduce with this either on Node 0.10:
var chai = require("chai");
chai.should();
var asP = require("chai-as-promised");
chai.use(asP);
var Promise = require("bluebird");
describe("The bug", function(){
it("doesn't reproduce", function(){
var promise = Promise.reject(Error());
return promise.should.be.rejected;
});
});
Doesn't reproduce in node either:
var Promise = require("bluebird");
var chai = require("chai");
var chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
chai.should();
var promise = Promise.reject(Error())
promise.should.be.rejected;
Tested 0.10, doesn't reproduce there either, maybe an old version of bluebird?
@ohjames was the OP - hopefully he'll come here and provide more information about his issue.
I don't recall a version of bluebird where a simple Promise.reject().then(..., handler) would ever have resulted in unhandled rejection being reported.. that's a pretty blatant bug.
Usually in these kind of scenarios the user has created multiple branches of rejected promises but has only one branch with a handler. Although in the code that is shown this is not the case...
I'd appreciate being left out of this one and to not be @-ed back into the thread given the hostility at the end of the original thread.
@ohjames this is the bluebird issue tracker, as long as you're respectful towards others here you may speak freely. If this is something we can solve for you from our side (regardless of it being our fault or not) we'd like to help.
Bluebird's design principle is to be pragmatic - if something can be fixed from our side there is a good chance that we'll fix it from our side. For us you're a user with a problem and we're interested in fixing it. If you moved on and don't want it fixed that's fine too - no hard feelings. Help in reproducing it is appreciated though.
As a side note, I understand @domenic and @keithamus's side too, leaving an issue calling a library crap is not a polite conversation starter. That discussion escalated and I'm sure both sides would have preferred it if it turned out differently. What's done is done and it'd be great to focus on solving your problem now :)
Closing since this issue was opened on behalf of @ohjames and he doesn't appear to be interested in providing the necessary reproduction steps.
I also think the original issue could have been handled better. @ohjames showed a blatant bug in basic functionality of bluebird but instead of telling the OP to open a bug report, the authors of chai-as-promised misled him that the issue is a feature of bluebird forcing @ohjames into a false dilemma of choosing between bluebird and chai-as-promised.
@petkaantonov i have similar issue in this code:
var Promise = require('bluebird');
var p = new Promise( function(resolve,reject){
try {
console.log('code');
throw new Error('test');
} catch(error) {
reject(error);
}
});
p.then(function(){
console.log('normal');
});
p.catch(function(reason){
console.log('catch', reason);
});
which causes the p.catch function to be called AND an Unhandled rejection Error to be printed.
Rewriting with p.then( function(){..}, function(){...}) does perform correctly. This is with version 3.0.5. Is this the expected behavior ? I was under the impression that the error callback argument should be functionally identical to the catch function callback.
@matthiasg The promise returned by p.then in the middle is the one that has unhandled rejection.
It should go away if you catch it:
var Promise = require('bluebird');
var p = new Promise( function(resolve,reject){
try {
console.log('code');
throw new Error('test');
} catch(error) {
reject(error);
}
});
p.then(function(){
console.log('normal');
}).catch(function(reason) {
console.log('phew, caught another error!', reason);
});
p.catch(function(reason){
console.log('catch', reason);
});
If you have single consumer scenario it makes no sense to divide the promise chains like above though. And if you have multiple consumers, then each consumer of course should handle their respective errors, in your case consumer 1 didn't handle it but only consumer 2 did.
ah .. sorry indeed the catch was not chained through... absolutely correct. it was a refactoring mistake in my code.
I'd recommend to simply use
var p = new Promise( function(resolve,reject) {
// try-catch is unnecessary in the promise executor
console.log('code');
throw new Error('test');
});
p.then(function() {
console.log('normal');
}, function(reason) {
console.log('catch', reason);
});
@petkaantonov
@keithamus
Doesn't reproduce in node either:
Actually, that example fails for me if I include it inside of an actual test:
var Promise = require('bluebird');
var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
chai.should();
describe('Test', function() {
it('should work but barks about unhandled rejections', function() {
var promise = Promise.reject(Error());
return promise.should.be.rejected;
});
});
Put it inside a test file and run with Mocha: ./node_modules/mocha/bin/_mocha "test.spec.js"
Result:
โ should work but barks about unhandled rejections
Unhandled rejection Error
at Context.<anonymous> (/Users/Adam/Sites/bookingpro/server/test.spec.js:10:34)
at callFn (/Users/Adam/Sites/bookingpro/server/node_modules/mocha/lib/runnable.js:343:21)
at Test.Runnable.run (/Users/Adam/Sites/bookingpro/server/node_modules/mocha/lib/runnable.js:335:7)
at Runner.runTest (/Users/Adam/Sites/bookingpro/server/node_modules/mocha/lib/runner.js:444:10)
at /Users/Adam/Sites/bookingpro/server/node_modules/mocha/lib/runner.js:550:12
at next (/Users/Adam/Sites/bookingpro/server/node_modules/mocha/lib/runner.js:361:14)
at /Users/Adam/Sites/bookingpro/server/node_modules/mocha/lib/runner.js:371:7
at next (/Users/Adam/Sites/bookingpro/server/node_modules/mocha/lib/runner.js:295:14)
at Immediate.<anonymous> (/Users/Adam/Sites/bookingpro/server/node_modules/mocha/lib/runner.js:339:5)
at runCallback (timers.js:637:20)
at tryOnImmediate (timers.js:610:5)
at processImmediate [as _immediateCallback] (timers.js:582:5)
Running it as a standalone script (without the describe and it wrappers) didn't trigger the error.
I'm getting these Unhandled rejection errors in a lot of my tests now that used to work fine, so it seems something has happened/changed that this problem has now returned.
This happens with Node 7.1.0 and 7.2.0 for me.
Any thoughts?
@adamreisnz At the risk of saying "works on my machine"... well...
$ cat test.js
```js
var Promise = require('bluebird');
var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
chai.should();
describe('Test', function() {
it('should work but barks about unhandled rejections', function() {
var promise = Promise.reject(Error());
return promise.should.be.rejected;
});
});
```bash
$ node -v
v7.2.1
$ npm i chai chai-as-promised mocha bluebird
/some/folder/somewhere
โโโ [email protected]
โโโ [email protected]
โโโ [email protected]
โโโ [email protected]
$ ./node_modules/.bin/_mocha test.js
Test
โ should work but barks about unhandled rejections
1 passing (17ms)
Hah, well yeah, that's fun.
Have retested it on both my computers now, and can't reproduce it anymore either.
Ran the test suite in our app, and it doesn't happen there anymore either.
Guess we'll file this under the Mulder & Scully category.
I experienced the same issue in a mocha unit test. Worth noting that the tests worked fine before, so I'm not sure if I ran an update at some point or changed the code. Using webpack to compile tests:
$ node -v
v6.6.0
$ /path/to/project
โโโ [email protected]
โโโฌ [email protected]
โโโ [email protected]
Here's my test:
it('should return a rejected promise if an invalid object is passed', (done) => {
postModel.createPost({})
.catch((error) => {
expect(error.name).to.equal('BadRequest');
done();
});
});
and the error is:
โ should return a rejected promise if an invalid object is passed
Unhandled rejection BadRequest: "objectType" is required, path: objectType
The issue is the error being thrown synchronously, not giving the promise a chance to register a catch block.
It happened to me again a little while ago, but it only occurred once (non-reproducible second and subsequent time around), so I didn't bother analysing further.
@adamreisnz FWIW, you may be experiencing https://github.com/domenic/chai-as-promised/issues/173
Looks like it could be the case, thanks. It happens quite irregularly though, so I am not too bothered by it at the moment.
I'm also seeing similar symptoms in https://github.com/sinonjs/sinon/pull/1225 for folks noticing this issue.
To make it more the sscce more reproducible, we added a delay between the declaration of the promise and the should.be.rejected.
Does it makes it easier to solve the problem ?
How to fix this problem?
Can we redirect the output of the console.log of the promise under test or redefine bluebird's rejection handler?
var Promise = require('bluebird');
var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
chai.should();
describe('Test', function () {
it('should work but barks about unhandled rejections', function () {
var promise = Promise.reject(Error());
return Promise.delay(1000).then(() => promise.should.be.rejected);
});
});
A solution to my own func:
var Promise = require('bluebird');
var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
chai.should();
describe('Test', function () {
it('should just work without barking', function () {
Promise.onPossiblyUnhandledRejection(( ) => {});
var promise = Promise.reject(Error());
return Promise.delay(1000)
.then(() => promise.should.be.rejected)
.then(() => {
Promise.onPossiblyUnhandledRejection(function (error) {
console.error(error);
});
});
});
it('should work but barks about unhandled rejections', function () {
var promise = Promise.reject(Error());
return Promise.delay(1000).then(() => promise.should.be.rejected);
});
});
I did this:
sinon.addBehavior('rejects', (stub, val) => {
// Bluebirds unhandled rejection works by checking if there is no error handler in any attached
// promise chain as the rejection fires, constructing the promise sync would trigger this
// mechanism as multiple ticks may pass before an error handler is attached.
let p;
stub.returns(new Proxy({}, {
get (obj, key) {
if (p) {
return p[key].bind(p);
}
p = Bluebird.reject(val);
return p[key].bind(p);
},
}));
});
Most helpful comment
Hah, well yeah, that's fun.
Have retested it on both my computers now, and can't reproduce it anymore either.
Ran the test suite in our app, and it doesn't happen there anymore either.
Guess we'll file this under the Mulder & Scully category.