11

I use $q.when to wrap around other lib promises. It works like a charm but when i try to run it inside Karma the promise failes to resolve (done() is never executed) even if I ran $digest and even after timeout. Here is sample code:

describe('PouchDB', function () {
var $q, $rootScope;

beforeEach(inject(function (_$rootScope_, _$q_) {
    $rootScope = _$rootScope_;
    $q = _$q_;
}));

it("should run", function (done) {

    function getPromise() {
        var deferred = Q.defer();

        deferred.resolve(1);

        return deferred.promise;
    }

    $q.when(getPromise())
        .then(function () {
            done();   // this never runs
        });

    $rootScope.$digest();
});

Why? What is the cause of this? I really cannot get it.

Update (workaround)

I do not understand why $q.when is not resolved in Karma - it has something with nextTick function but I cannot debug the problem. Instead I ditched $q.when and wrote simple function that converts PouchDB (or any other like Q) to $q:

.factory('$utils', function ($q, $rootScope) {
  return {
    to$q: function (promise) {
      var deferred = $q.defer();

      promise.then(function (result) {
        deferred.resolve(result);
        $rootScope.$digest();
      });

      promise.catch(function (error) {
        deferred.reject(error);
        $rootScope.$digest();
      });

      return deferred.promise;
    }
  }
})
Yoorek
  • 1,003
  • 1
  • 12
  • 30
  • Why are you using Q and $q together? Maybe Karma (or the AngularMocks lib that it uses) is expecting promises generated by $q.defer() instead of Q.defer() – Sunil D. Jun 15 '14 at 16:50
  • I use PouchDB which has it's own promises, so I need to wrap them with $q. I used Q in sample just to simplify things - result though is the same : $q.when is not resolved in Karma when it wraps external promises which is BTW main purpose of $q.when – Yoorek Jun 15 '14 at 17:06

2 Answers2

22

From How to resolve $q.all promises in Jasmine unit tests? it seems the trick is:

$rootScope.$apply();

I just had the same problem and this works for me; the promises are resolved on making this call.

Community
  • 1
  • 1
Andrew Magee
  • 6,506
  • 4
  • 35
  • 58
1

I've adjusted variable and injected dependency names on this to keep things clear as test writing continues. If done() is a function inside your (controller? directive? service/factory?) then it should be called when the test runs without trying to pass it in as a dependency. Ideally done() should be spied upon, but without knowing where it comes from it is impossible to show you how to set up the spy function.

The only other detail missing is that you have no expect() in this test suite. Without it I have no way to know what you are expecting to be asserted.

describe('PouchDB', function () {
    var scope, db, q, rootScope;

    beforeEach(
        inject(
            function(_$rootScope_, _$q_){
                rootScope = _$rootScope_;
                scope = rootScope.$new();
                q = _$q_;
            }
        )
    );

    it("should run", function(){

        //spy should be constructed here

        function getPromise() {
            var deferred = q.defer();

            deferred.resolve(1);

            return deferred.promise;
        }

        q.when(getPromise)
            .then(function () {
                done();
            });

        scope.$digest();

        //assertion should be here

    });
});
MBielski
  • 6,628
  • 3
  • 32
  • 43