Sunday, November 19, 2017

Stub a service in test in an Angular 4 app.

I just got this working today in a GitHub project you may see here. You will want to use basic IoC dependency injection in your outermost wrapper module as suggested here like so:

providers: [
   Configuration,
   {provide: IpContract, useClass: IpService}
]

 
 

The Configuration above is a pseudoWeb.config as suggested here. It is used in my service like so:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Configuration } from '../../configuration';
import { HttpClient } from '@angular/common/http';
import { IpAddress } from '../models/ipAddress.model';
import { IpContract } from '../contracts/ip.contract';
@Injectable()
export class IpService implements IpContract {
   constructor(private httpClient: HttpClient, private configuration: Configuration) { }
   
   getIp():Observable<IpAddress>{
      let route: string = this.configuration.routeToApi + "api/ip";
      return this.httpClient.get<IpAddress>(route,{});
   }
}

 
 

Our contract looks like this:

import { Observable } from 'rxjs/Observable';
import { IpAddress } from '../models/ipAddress.model';
export class IpContract {
   constructor() {}
   public getIp: () => Observable<IpAddress>;
}

 
 

We may just stub our contract like so:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map';
import { IpAddress } from '../models/ipAddress.model';
import { IpContract } from '../contracts/ip.contract';
@Injectable()
export class IpContractStub implements IpContract {
   constructor() { }
   
   getIp():Observable<IpAddress>{
      let ipAddress:IpAddress = new IpAddress();
      ipAddress.Ip = "127.0.0.1";
      return Observable.of(new IpAddress()).map(o => ipAddress);
   }
}

 
 

We are going to test a component that has a simple template like so:

<section>{{greeting}}</section>

 
 

...and looks like this on the TypeScript side:

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/observable';
import { IpAddress } from '../models/ipAddress.model';
import { IpContract } from '../contracts/ip.contract';
@Component({
   selector: 'ip',
   templateUrl: './ip.component.html'
})
export class IpComponent implements OnInit {
   greeting:string;
   ipAddress:IpAddress;
   
   constructor(public ipContract: IpContract) {
   
   }
   
   ngOnInit(): void{
   this.ipContract.getIp().toPromise().then(
      function(data) {
         this.ipAddress = data;
         this.greeting = "Welcome " + this.ipAddress.Ip + " to Grover Cleveland Hopscotch!";
      }.bind(this),
      function(error){
         console.log(error);
      });
   }
}

 
 

Out test:

import { TestBed, fakeAsync, tick, async, ComponentFixture, inject } from '@angular/core/testing';
import { IpComponent } from './ip.component';
import { IpContract } from '../contracts/ip.contract';
import { IpContractStub } from '../contracts/ip.contract.stub';
import { ActivatedRoute } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { Observable } from 'rxjs/Observable';
 
describe('IpComponent (shallow)', () => {
   let fixture: ComponentFixture<IpComponent>;
   let component: IpComponent;
   let element;
   
   beforeEach(() => {
      TestBed.configureTestingModule({
         imports: [
            FormsModule
         ],
         declarations: [
            IpComponent
         ],
         providers: [
            {provide: IpContract, useValue: new IpContractStub()}
         ]
      });
      fixture = TestBed.createComponent(IpComponent);
      component = fixture.componentInstance;
      element = fixture.nativeElement;
   });
   
   describe('greeting', ()=> {
      it('should contain ip address', fakeAsync(()=> {
         
         fixture.detectChanges();
         tick();
         fixture.detectChanges();
         fixture.whenStable().then(() => {
            fixture.detectChanges();
            expect(element.querySelector('section').textContent).toContain('Welcome
                  127.0.0.1 to Grover Cleveland Hopscotch!');
         });
      }));
   });
});

No comments:

Post a Comment