Categories
Uncategorized

Testing Component with Service (Promise) in Angular

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.