angular: Async in unit tests does not handle unresolved promises

I’m submitting a…


[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report  
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

Writing tests using async does not seem to wait for all promises to resolve.

it('should fail', async(() => {
    const promise = new Promise(() => {});
    promise.then(() => {
        expect(false).toBe(true);
    });
  }));

This test should fail, but because the promise is never resolved and the test does not wait for it to be resolved it passes as success.

Expected behavior

The test should fail or timeout because the promise is never resolved.

Base on the documentation for async test function here https://angular.io/api/core/testing/async and https://angular.io/guide/testing it seems like the intended behaviour of async is to not complete the test until all async calls are completed.

Not sure if this is a documentation problem or a bug with unresolved promises. If this is the intended behaviour it should be described more clearly what situations async can actually be used in and what types of async calls it handles.

Minimal reproduction of the problem with instructions

See current behaviour for a simple example using a promise that is never resolved.

This looks to be the same issue as observed in a issue from 2.0 beta when async was introduced https://github.com/angular/angular/issues/8736. But the submitter closed the issue when he found a workaround by not using async.

What is the motivation / use case for changing the behaviour?

It currently seems unsafe to use async in tests when one is never sure if a test passed because there was no problems or because the async expect was never run. Currently recommended our developers to stay away from async and write tests using fakeAsync so they can be written in a sync way to ensure all expects are actually executed.

Environment


Angular version: 5.0.5

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 20 (11 by maintainers)

Most upvoted comments

@webmaster128 , @kenborge , @gkalpak , in the next version of zone.js (0.8.21), this issue will be fixed. https://github.com/angular/zone.js/pull/1014

the test cases such as

it('should fail', async(() => {
    const promise = new Promise(() => {});
    promise.then(() => {
        expect(false).toBe(true);
    });
  }));

and

it('async should work', async(inject([MyService], (service: MyService) => {
  (async () => {
    let nonResolvingPromise = new Promise((resolve, reject) => {
      // I never call resolve() or reject()
    });
    await nonResolvingPromise;

    fail("should fail")
  })();
})));

will work as expected.

Thanks @JiaLiPassion, worked like a charm!

Test:

describe('this test', () => {
  it('should fail', async(() => {
    const promise = new Promise((_, reject) => reject(new Error('test')));
  }));
});

Result:

this test > should fail
Failed: Uncaught (in promise): Error: test
Error: test
    at http://localhost:9876/_karma_webpack_/src/app/shared/components/affidavit-table.component.spec.ts:38:55
    at new ZoneAwarePromise (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone.js:931:1)
    at UserContext.<anonymous> (http://localhost:9876/_karma_webpack_/src/app/shared/components/affidavit-table.component.spec.ts:38:21)
    at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone.js:391:1)
    at AsyncTestZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.AsyncTestZoneSpec.onInvoke (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone-testing.js:770:1)
    at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone-testing.js:305:1)
    at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone.js:390:1)
    at Zone../node_modules/zone.js/dist/zone.js.Zone.runGuarded (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone.js:161:1)
    at runInTestZone (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone-testing.js:898:1)
    at UserContext.<anonymous> (http://localhost:9876/_karma_webpack_/node_modules/zone.js/dist/zone-testing.js:832:1)

Looking forward to seeing what you’re working on 😃

@gkalpak , sure, with the latest version of zone.js, and to support this feature, we need a flag in test.ts.

(window as any).['__zone_symbol__supportWaitUnResolvedChainedPromise'] = true;

then the case will work as expected. I will add it to doc later.