This post presents a pattern for flexibly replacing a concrete or 'real' Angular 2 service module (that talks to a Web API) with a *mock* service module.
The goal is to have an Angular 2 app that is testable WITHOUT having to connect to a real Web API.
This can be useful for dev-testing, and for unit tests.
I've used the pattern with Angular 2 RC4 and for 2.1 .. 2.4
/////////////////////////////////////////
//app.component.ts
//
//an app that uses dependency injection to automatically inject a REAL or a MOCK ng2 'car' service provider, according to the running environment
import { Component, OnInit } from '@angular/core';
import { BaseProxyCarService, ProxyCarProviderFactory } from './car-service-proxy/index';
@Component({
moduleId: module.id,
selector: 'app-car-list',
providers: [ProxyCarProviderFactory.getActiveProvider({
// OPTIONAL: pass a param here to FORCE using the REAL web service on the main site.
// normally this should be false.
isUsingLocalMainSiteForWebServices: false
})],
templateUrl: './app.component.html',
})
// Component class
export class CarListComponent implements OnInit {
// Constructor with injected service
constructor( private _carService: BaseProxyCarService ) {
}
// Get all car data
loadCars() {
this._carService.getRecentCars()
.subscribe(
cars => {
//do something with the cars ...
},
err => console.log(err)
);
}
ngOnInit() {
this.loadProjects();
}
}
/////////////////////////////////////////
//proxy-myservice-provider.factory.ts
//ProxyCarService is a standard Angular 2 service module that knows how to talk to a 'Car' Web API that provides Car data.
//MockProxyCarService is a mock version of that service, that does NOT talk to the real Web API
//Both ProxyCarService and MockProxyCarService derive from an abstract class BaseProxyCarService, so that they can be injected via the provider (ProxyCarProviderFactory).
import { BaseProxyCarService } from './base-proxy-car.service';
import { MockProxyCarService } from './mock-proxy-car.service';
import { ProxyCarService } from './proxy-car.service';
export class EnvironmentUtilsSettings {
/**
// set to true, for debugging against REAL web services on main site
// (requires turning OFF CORS prevention in Chrome):
*/
isUsingLocalMainSiteForWebServices: boolean = false;
}
// The provider, which provides the correct derivation of BaseProxyCarService according to the environment.
// note: both MockProxyCarService and ProxyCarService would derive from the abstract class BaseProxyCarService
export class ProxyProjectProviderFactory {
public static getActiveProvider(settings: EnvironmentUtilsSettings): any {
let provider = { provide: BaseProxyCarService, useClass: ProxyCarService };
let mockProvider = { provide: BaseProxyCarService, useClass: MockProxyCarService };
// we are running in a dev server outside of the main site, so we cannot use real web api due to Cors
// so to enable dev testing, we use a mock service:
let activeProvider = ProxyProjectProviderFactory.isInDevServer(settings) ? mockProvider : provider;
return activeProvider;
}
//Determines whether we are running on a site that DOES have web services.
//Alternative logic could be to check are we running under unit test (under karma for example)
public static isInDevServer(settings: EnvironmentUtilsSettings): boolean
{
let origin = window.location.origin;
//here the local main site when developing WITH web services is at port 5000
//if we are on localhost but NOT at port 5000 then we are only running a smaller site WITHOUT the web services
let isSiteUrlDevServer = origin.indexOf('localhost:') >= 0
&& origin.indexOf('localhost:5000') < 0;
if (settings.isUsingLocalMainSiteForWebServices) {
return false;
}
return isSiteUrlDevServer;
}
}
The goal is to have an Angular 2 app that is testable WITHOUT having to connect to a real Web API.
This can be useful for dev-testing, and for unit tests.
I've used the pattern with Angular 2 RC4 and for 2.1 .. 2.4
/////////////////////////////////////////
//app.component.ts
//
//an app that uses dependency injection to automatically inject a REAL or a MOCK ng2 'car' service provider, according to the running environment
import { Component, OnInit } from '@angular/core';
import { BaseProxyCarService, ProxyCarProviderFactory } from './car-service-proxy/index';
@Component({
moduleId: module.id,
selector: 'app-car-list',
providers: [ProxyCarProviderFactory.getActiveProvider({
// OPTIONAL: pass a param here to FORCE using the REAL web service on the main site.
// normally this should be false.
isUsingLocalMainSiteForWebServices: false
})],
templateUrl: './app.component.html',
})
// Component class
export class CarListComponent implements OnInit {
// Constructor with injected service
constructor( private _carService: BaseProxyCarService ) {
}
// Get all car data
loadCars() {
this._carService.getRecentCars()
.subscribe(
cars => {
//do something with the cars ...
},
err => console.log(err)
);
}
ngOnInit() {
this.loadProjects();
}
}
/////////////////////////////////////////
//proxy-myservice-provider.factory.ts
//ProxyCarService is a standard Angular 2 service module that knows how to talk to a 'Car' Web API that provides Car data.
//MockProxyCarService is a mock version of that service, that does NOT talk to the real Web API
//Both ProxyCarService and MockProxyCarService derive from an abstract class BaseProxyCarService, so that they can be injected via the provider (ProxyCarProviderFactory).
import { BaseProxyCarService } from './base-proxy-car.service';
import { MockProxyCarService } from './mock-proxy-car.service';
import { ProxyCarService } from './proxy-car.service';
export class EnvironmentUtilsSettings {
/**
// set to true, for debugging against REAL web services on main site
// (requires turning OFF CORS prevention in Chrome):
*/
isUsingLocalMainSiteForWebServices: boolean = false;
}
// The provider, which provides the correct derivation of BaseProxyCarService according to the environment.
// note: both MockProxyCarService and ProxyCarService would derive from the abstract class BaseProxyCarService
export class ProxyProjectProviderFactory {
public static getActiveProvider(settings: EnvironmentUtilsSettings): any {
let provider = { provide: BaseProxyCarService, useClass: ProxyCarService };
let mockProvider = { provide: BaseProxyCarService, useClass: MockProxyCarService };
// we are running in a dev server outside of the main site, so we cannot use real web api due to Cors
// so to enable dev testing, we use a mock service:
let activeProvider = ProxyProjectProviderFactory.isInDevServer(settings) ? mockProvider : provider;
return activeProvider;
}
//Determines whether we are running on a site that DOES have web services.
//Alternative logic could be to check are we running under unit test (under karma for example)
public static isInDevServer(settings: EnvironmentUtilsSettings): boolean
{
let origin = window.location.origin;
//here the local main site when developing WITH web services is at port 5000
//if we are on localhost but NOT at port 5000 then we are only running a smaller site WITHOUT the web services
let isSiteUrlDevServer = origin.indexOf('localhost:') >= 0
&& origin.indexOf('localhost:5000') < 0;
if (settings.isUsingLocalMainSiteForWebServices) {
return false;
}
return isSiteUrlDevServer;
}
}
Comments
Post a Comment