Glenn Mohammad
Glenn Mohammad

Reputation: 4683

AngularJS unit testing: using async/await with Jasmine

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
  .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 😞

Upvotes: 1

Views: 2143

Answers (2)

Frank Modica
Frank Modica

Reputation: 10516

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);
    });
  });
}

Upvotes: 3

Estus Flask
Estus Flask

Reputation: 222309

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();
});

Upvotes: 0

Related Questions