I just started writing an app using Spring Boot/Angular 2 combo. Once I had the initial demo up and working and cracked wiring in Spring security (another blog entirely), my attention turned to writing some tests that would drive the next phases.
My Spring Boot back-end can be unit/integration tested on it's own. I was interested in making sure the user experience was tested. Tradionally I might have turned to Geb for a full server side project, but this is mostly just javascript and I know how fast Jasmine/Karma tests run, so there must be a nice way of doing this?
Indeed I was not disappointed to find that Angular ng new webapp
had already created me a e2e folder with a skeleton Protractor test to point the way.
But how to stub out the back-end? I have used many mocking frameworks before; Sinon, for example, is excellent in unit tests. There must by a simple way of doing it for Protractor/Angular.
However, I was astonished to find this issue that has been open for over 2 years suggesting that there is no mechanism!
So I thought I would write this post about how I overcame this problem using an old favourite of mine re-written for javascript: Wiremock.
With the Spring Boot back-end my URLs are all relative as the app is all hosted together, so first I needed a way to call out to wiremock instead.
So I created a interceptor: src/main/webapp/src/app/interceptors/base-url.interceptor.ts
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from "@angular/common/http";
import {Observable} from "rxjs/Observable";
import {Injectable} from "@angular/core";
import {environment} from '../../environments/environment';
@Injectable()
export class BaseUrlInterceptor implements HttpInterceptor {
constructor() {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const apiReq = request.clone({url: `${environment.baseUrl}${request.url}`});
return next.handle(apiReq);
}
}
Don't forgot to include it in your providers!
providers: [
...
{ provide: HTTP_INTERCEPTORS, useClass: BaseUrlInterceptor, multi: true }
],
And gave prod evironment (src/main/webapp/src/environments/environment.prod.ts
) an empty base url:
export const environment = {
production: true,
baseUrl: ''
};
And my non-prod environment (src/main/webapp/src/environments/environment.ts
) the wiremock url:
export const environment = {
production: false,
baseUrl: 'http://localhost:9999'
};
After installing wiremock (npm install jswiremock --save-dev
), you can then create a wrapper for it (not really necessary, but it makes the tests cleaner)
src/main/webapp/e2e/wiremock.ts
let jswiremocklib = require('jswiremock');
export class Wiremock {
jswiremock;
constructor() {
this.jswiremock = new jswiremocklib.jswiremock(9999); //note the port here matches the port in the baseUrl
}
stop() {
this.jswiremock.stopJSWireMock();
}
get(url: string, body: object) {
jswiremocklib.stubFor(this.jswiremock, jswiremocklib.get(jswiremocklib.urlEqualTo(url))
.willReturn(jswiremocklib.a_response()
.withStatus(200)
.withHeader({"Content-Type": "application/json"})
.withBody(JSON.stringify(body))));
}
}
import {AppPage} from './app.po';
import {Wiremock} from "./wiremock";
describe('App', () => {
let page: AppPage;
let wiremock: Wiremock;
beforeEach(() => {
page = new AppPage();
wiremock = new Wiremock();
});
afterEach(() => {
wiremock.stop();
});
it('should login', () => {
page.navigateTo();
page.login().click().then(function () {
wiremock.get("/api/user", {name: "fred"});
});
page.sendCredintials("user", "password");
expect(page.logout().isPresent()).toBeTruthy();
});
});
The only tricky part here is stubbing the call at the correct time, if you do it too early, you may find your test is already logged in before it even starts.
Hope it helps!
@gaeljw I am trying the same above, my api calls are not getting mocked, also not getting any error. Did you face any problem while implementing the above and if you have fixed any, can you please share it here?