1

I'm trying to unit testing my Angular service using async/await keyword in the corresponding (Jasmine) unit tests below. The test for the native Promise works just fine, but I'm pretty much stuck in making the Angular $q counterpart work.

  • Angular: 1.6.5
  • Jasmine: 2.7.0
  • (Headless) Chrome on MacOS: 60.x

angular
  .module('asyncAwaitTest', [])
  .factory('xService', xServiceFactory);

function xServiceFactory(
  $q,
  $timeout
) {
  return {
    getXAfter1Sec() {
      return new Promise(resolve => setTimeout(() => resolve(43), 1000));
    },
    getXAfter1SecWithAngular$Q() {
      const deferred = $q.defer();

      $timeout(() => deferred.resolve(43), 1000);

      return deferred.promise;
    }
  };
}

jasmine.DEFAULT_TIMEOUT_INTERVAL = 2000;

describe('asyncAwaitTest: x service', () => {
  let $timeout;
  let xService;

  beforeEach(() => {
    module('asyncAwaitTest');

    inject(
      (
        _$timeout_,
        _xService_
      ) => {
        $timeout = _$timeout_;
        xService = _xService_;
      }
    );
  });

  it('should work', async (done) => {
    const x = await xService.getXAfter1Sec();

    expect(x).toEqual(43);

    done();
  });

  it('should work, too. but y not?!!', async (done) => {
    const xPromise = xService.getXAfter1SecWithAngular$Q();

    $timeout.flush();

    const x = await xPromise;

    expect(x).toEqual(43);

    done();
  });
});

Fiddle provided here: https://jsfiddle.net/glenn/gaoh6bvc/

I've tried Google but it doesn't give me a significant good lead 😞

2
  • async/await don't know about $q. They work with native promises. Check this out: stackoverflow.com/questions/35629246/… Commented Jul 30, 2017 at 18:34
  • I see that. But is there any workaround? Putting .run(function runBlock($window, $q) { $window.Promise = $q; }); only making it worse - it fails both tests :/ Commented Jul 30, 2017 at 19:22

2 Answers 2

3

You could create a helper for your test that converts the promise from $q to a native promise. Check it out here.

it('should work, too. but y not?!!', async (done) => {
  const xPromise = toNativePromise(xService.getXAfter1SecWithAngular$Q());

  $timeout.flush();

  const x = await xPromise;

  expect(x).toEqual(43);

  done();
});

function toNativePromise(promise) {
  return new Promise((resolve, reject) => {
    promise.then(val => {
      resolve(val);
    }, err => {
      reject(err);
    });
  });
}
Sign up to request clarification or add additional context in comments.

1 Comment

I think this is an acceptable workaround for the moment. Thanks! :)
0

async functions are based on native promises, while AngularJS uses $q promises. await is a syntactic sugar for chaining a promise with then. $q promise chains are executed on digest in tests.

This cannot be fixed with

await xPromise;
$rootScope.$digest();

because $rootScope.$digest() isn't evaluated until $rootScope.$digest() is executed. This results in pending promise.

AngularJS shouldn't be tested with async..await in the first place. Angular was designed to be tested synchronously.

It is

it('...', () => {
  ...
  xPromise.then(x => {
    expect(x).toEqual(43);
  });
  $rootScope.$digest();
});

Or promises can be flattened with jasmine-promise-matchers:

it('...', () => {
  ...
  expect(xPromise).toBeResolvedWith(43);
  $rootScope.$digest();
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.