Search code examples
angularunit-testingjasmine

Angular Jasmine Test: SPEC HAS NO EXPECTATIONS when using setTimeout


I'm trying to write a unit test for a components in my App. It's a very simple component called TroubleShootingPage that is only supposed to show a help page, if someone has trouble somewhere in the rest of my app.

All tests are passing, but in the output in Karma I see a hint: SPEC HAS NO EXPECTATIONS:

My Karma Output

The test that supposedly has no expectations is dealing with the function getTroubleShootingPage() in my TroubleShootingPage Component, which looks like this:

export interface Entry<T> {
    sys: Sys;
    fields: T;
    toPlainObject(): object;
    update(): Promise<Entry<T>>;
}

troubleShootingPage: Entry<any>;

constructor(private contentfulService: ContentfulService) {}

ngOnInit() {
    this.getTroubleShootingPage("en-US")
}

getTroubleShootingPage(locale: string) {
    this.contentfulService.getTroubleShootingPage(locale).then(page => {
      this.troubleShootingPage = page;
      ...
  }
}

As you can see, the content/text of the troubleshooting page has to be loaded from the contentfulService and there it is loaded from an API (Contentful) depending on the user's language. So I only want to secure that the function gets called and that the returned input from the contentfulService gets correctly assigned to the variable this.troubleShootingPage.

The test looks like this:

describe('TroubleshootingPageComponent', () => {
    let component: TroubleshootingPageComponent;
    let fixture: ComponentFixture<TroubleshootingPageComponent>;
    
    let contentfulServiceStub: Partial<ContentfulService> = {
        getTroubleShootingPage: () => {
            return new Promise<Entry<any>>(() => {
                return troubleShootingPageMock;
            });
        }
    };

    let troubleShootingPageMock: Entry<any>;
    troubleShootingPageMock = {
        fields: {
            description: "Please read here for further help:",
            mainText: {},
            textCancelButton: "Quit",
            textConfirmButton: "Continue",
            title: "Need Help?"
        },
        toPlainObject() {
            return null;
        },
        update() {
            return null;
        },
        sys: {
            type: "",
            id: "string",
            createdAt: "string",
            updatedAt: "string",
            locale: "string",
            contentType: {
                sys: null
            },
        },
    };

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            imports: [],
            declarations: [TroubleshootingPageComponent],
            schemas: [NO_ERRORS_SCHEMA],
            providers: [{ provide: ContentfulService, useValue: contentfulServiceStub }]
        })
            .compileComponents();
    }));

    beforeEach(() => {
        fixture = TestBed.createComponent(TroubleshootingPageComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
    });

   it('should get the troubleShootingPage form Contentful', () => {
        component.getTroubleShootingPage("en-GB");
        setTimeout(() => {
            expect(component.troubleShootingPage).toEqual(troubleShootingPageMock.fields);
        }, 1000)
    });

    // I also tried:
    // it('should get the troubleShootingPage form Contentful', (done) => {
    //     component.getTroubleShootingPage("en-GB");
    //     setTimeout(() => {
    //         expect(component.troubleShootingPage).toEqual(troubleShootingPageMock.fields);
    //         done();
    //     }, 1000)
    // });
});

I added a timeout with setTimeout() because without it component.troubleShootingPage was always undefined, because the test went to the ecpect() statement, before the Promise in the getTroubleShootingPage() function was resolved:

it('should get the troubleShootingPage form Contentful', () => {
            component.getTroubleShootingPage("en-GB");
            expect(component.troubleShootingPage).toEqual(troubleShootingPageMock.fields);            
});

-> this fails, because component.troubleShootingPage is undefined.

How could I solve this, any ideas?


Solution

  • Ok thanks for the comments, the hint with making the function return the promise was the necessary part:

    in the component I made one function for only returning the promise:

     getTroubleShootingPage(locale: string) {
        this.getTroubleShootingPageFromService(locale).then(page => {
          this.troubleShootingPage = page;
       })
    }
    
    getTroubleShootingPageFromService(locale: string) {
        return this.contentfulService.getTroubleShootingPage(locale);
    }
    

    And in my test I have now this, which works fine:

    it('should get the troubleShootingPage form Contentful', async () => {
            spyOn(contentfulServiceStub, 'getTroubleShootingPage').and.returnValue(Promise.resolve(troubleShootingPageMock))
            await component.getTroubleShootingPage("en-US");
            expect(component.troubleShootingPage).toEqual(troubleShootingPageMock);
        });