Home > Back-end >  Testing promise resolving returned from service
Testing promise resolving returned from service

Time:05-11

I've got a problem with testing promise resolving of promise returned from function. The problem is that .then() isn't executed.

Components method that I'm testing:

 private _openToast(): void {
        this._toast
            .action()
            .then(
                () => {
                    this.test();
                },
                () => {}
            );
    }
    
test(): void {
console.log("It's ok")
}

Service

@Injectable()
export class ToastService {
    constructor(private _toast: OuterToastService) {}

    action<ValueType>(context?: Partial<IToastContext>, config?: Partial<ToastConfig>): Promise<ValueType> {
        return this._toast.open(); // It's material-like toast service that returns promise
    }
}

And test

describe('Component', () => {
    let component: Component;
    let fixture: ComponentFixture<Component>;
    const MockToastService = jasmine.createSpyObj('toast', ['action']);
    beforeEach(() => {
        TestBed.configureTestingModule({
            declarations: [Component],
            providers: [
                { provide: ToastService, useValue: MockToastService },
            ],
        });

        fixture = TestBed.createComponent(Component);
        component = fixture.componentInstance;
    });
    })

it('should call test() if toast accepted', () => {
            MockToastService.action.and.returnValue(Promise.resolve());
            const spy = spyOn<any>(component, 'test');

            component['_openToast']();
            expect(spy).toHaveBeenCalled();
        });

Result of running test is: Expected spy test to have been called. Which means (I believe so) that .then() isn't executed. Why?

CodePudding user response:

Which means (I believe so) that .then() isn't executed. Why?

Your test is entirely synchronous, so it's not giving time for this.test() to be called. The promise returned by MockToastService may be in a resolved state, but the .then callback still runs asynchronously, the next time microtasks are run.

To fix this, you'll need to make openToast return a promise, and then have your test wait for that promise:

private _openToast(): void {
  return this._toast.action().then(
    () => {
      this.test();
    },
    () => {}
  );
}

it("should call test() if toast accepted", () => {
  MockToastService.action.and.returnValue(Promise.resolve());
  const spy = spyOn<any>(component, "test");

  return component["_openToast"]().then(() => {
    expect(spy).toHaveBeenCalled();
  });
});

Alternatively, you can use a fakeAsync test, and then force the microtask queue to flush:

import { fakeAsync, flushMicrotasks } from '@angular/core/testing';

it("should call test() if toast accepted", fakeAsync(() => {
  MockToastService.action.and.returnValue(Promise.resolve());
  const spy = spyOn<any>(component, "test");

  component["_openToast"]();

  flushMicrotasks();

  expect(spy).toHaveBeenCalled();
}));
  • Related