To perform unit testing on a component that makes use of a service, that returns a promise, we can use fakeAsync() and spy() methods from Angular’s unit testing package.
user.component.ts
import { Component, OnInit } from '@angular/core'; import { UserService } from './user.service'; @Component({ selector: 'user-name', templateUrl: './user.component.html', styleUrls: ['./user.component.scss'] }) export class UserComponent implements OnInit { userName = ''; constructor(private userService: UserService) { } ngOnInit(): void { this.userService.getUserName().then(response => this.userName = response); } }
user.service.ts
import {Injectable} from '@angular/core'; import {HttpClient} from '@angular/common/http'; import 'rxjs/add/operator/toPromise'; @Injectable() export class UserService { constructor(private http: HttpClient) { } getUserName(): Promise<String> { return this.http.get('http://localhost:3000/user/') .toPromise(); } }
user.component.html
<div class="user"> {{userName}} </div>
By default in the angular testing framework, changes in data aren’t reflected in the template (view) by default unless detectChanges( ) is explicitly called on the component.
user.component.spec.ts
import { async, fakeAsync, tick, ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { DebugElement } from '@angular/core'; import { UserComponent } from './user.component'; import { UserService } from './user.service'; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ UserComponent ], providers: [ UserService ] }) .compileComponents(); // compile external template and scss })); // Synchronous section beforeEach(() => { fixture = TestBed.createComponent(UserComponent); comp = fixture.componentInstance; // Inject UserService into the component userService = fixture.debugElement.injector.get(UserService); // Setup spy on the `getUserName` method spy = spyOn(userService, 'getUserName') .and.returnValue(Promise.resolve(userName)); // Get the Username element from the template DOM structure un = fixture.debugElement.query(By.css('.user')); elem = un.nativeElement; }); it('should show username upon call return', fakeAsync(() => { fixture.detectChanges(); // update view with data tick(); // wait for all asynchronous calls to complete fixture.detectChanges(); // update view with data expect(elem.textContent).toBe(userName); }));
Note
tick( ) also takes milliseconds as argument, to wait for that time instead of waiting for all async calls to return.