bluebird: random false positive "Unhandled rejection Error" after upgrading to 3.5.1

I’ve this weird issue with jasmine testing framework and bluebird: it sometimes (not consistently) produce errors in the console log like this one:

Unhandled rejection Error: dummy
    at UserContext.<anonymous> (/home/mastro/ws/my-project-path/spec/unit/logic.spec.js:246:89)
    at UserContext.arguments.(anonymous function) (/home/mastro/ws/my-project-path/node_modules/jasmine-promises/dist/jasmine-promises.js:35:30)
    at attempt (/home/mastro/ws/platform/nsp-gamification-loyalty-services/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:4297:26)
    at QueueRunner.run (/home/mastro/ws/platform/nsp-gamification-loyalty-services/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:4217:20)
    at Timeout.runNext [as _onTimeout] (/home/mastro/ws/platform/nsp-gamification-loyalty-services/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:4257:20)
    at tryOnTimeout (timers.js:228:11)
    at Timer.listOnTimeout (timers.js:202:5)

Those are actually printed in the logs but do not make the test fail. In fact the test succeed, a test exhibiting the issue may look something like this:

        it('should throw if the check of `type` and `config` throws', () => {
            spyOn(actionHandler, 'validateActionConfig').and.returnValue(Promise.reject(new Error('dummy'))); 
            return sut.createAction(principal, action)
                .then(() => {
                    fail('Promise rejection expected, instead fulfilled');
                })
                .catch((err) => {
                    expect(actionHandler.validateActionConfig).toHaveBeenCalled();
                    expect(err.message).toBe('dummy');
                });
        });

The spyOn is jasmine way of testing mocked dependencies. sut is the subject under test. This test check our function createAction forward any rejectiong error from the call to actionHandler.validateActionConfig() by returning a rejection promise when the function is called. That promise is then catched, as you can see, and we also check the error is exactly the one throw there. So the promise IS catched, but somehow it is detected as uncatched.

Furthermore this is not an on/off problem. Some test randomly exhibit this behavior, some never do.

This didn’t happen with bluebird 3.5.0 and, according to your 3.5.1 release note (https://github.com/petkaantonov/bluebird/releases/tag/v3.5.1) you changed something in the Unhandled Promise rejection. Can you shed some light on what’s possibly going on? Thanks!

related dependencies

├── bluebird@3.5.1
│
├─┬ jasmine@2.8.0
│ └── exit@0.1.2
├── jasmine-core@2.8.0
├─┬ jasmine-expect@3.7.1
│ └── add-matchers@0.5.0
├── jasmine-promises@0.4.1
├─┬ jasmine-spec-reporter@3.3.0
│ └── colors@1.1.2

and…

$ node -v
v6.11.2
$ npm -v
3.10.10

I initially posted this as a comment to an old issue here https://github.com/petkaantonov/bluebird/issues/493 and opened a bug to jasmine here: https://github.com/jasmine/jasmine-npm/issues/124

But after some further testing with different versions of jasmine and bluebird I’m fairly certain this is introduced by a change in bluebird.

The problem DID NOT happen on bluebird version 3.5.0. And I think this problem is related to https://github.com/petkaantonov/bluebird/issues/1404

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 4
  • Comments: 16 (6 by maintainers)

Commits related to this issue

Most upvoted comments

@danielesegato I came up against this same issue in my Jasmine test

I fixed it by using andCallFake instead of returnValue

spyOn(actionHandler, 'validateActionConfig').and.callFake(() => Promise.reject(new Error('dummy')));

My thought is that returnValue immediately evaluates the result, so Node thinks your test case is rejecting a promise.

@petkaantonov I don’t think this is for us to fix to be honest - this is something the test framework should take care of.

  • Fixing in node: just use require(“timers”) to get reference to the actual setTimeout
  • Fixing in browsers: create iframe, attach and remove it from DOM and get reference to actual setTimeout from iframe.contentWindow.setTimeout