问题
I am trying to test HttpInterceptor that logs the response of the http
request. I have a log service that logs the response of the request. The interceptor logs only for GET requeststs.
Here's my interceptor:
import { HttpInterceptor, HttpHandler, HttpEvent, HttpRequest, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { LogService } from './log.service';
import { Injectable } from '@angular/core';
@Injectable({providedIn: 'root'})
export class LoggerInterceptor implements HttpInterceptor {
constructor(private _log: LogService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req)
.pipe(
tap(event => {
if (event instanceof HttpResponse) {
if (req.method === 'GET') {
this._log.log('I was logged');
}
}
})
);
}
}
Here's the spec file:
import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController, TestRequest } from '@angular/common/http/testing';
import { HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http';
import { HeaderInterceptor } from './add.header.interceptor';
import { LogService } from './log.service';
import { LoggerInterceptor } from './logger.interceptor';
const posts: Array<any> = [
{
'userId': 1,
'id': 1,
'title': 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',
'body': 'quia et suscipit\nsuscipit recusandae consequuntur expedita et '
},
{
'userId': 1,
'id': 2,
'title': 'qui est esse',
'body': 'est rerum tempore vitae\nsequi sint nihil reprehenderit dolor b'
}
];
describe('HeaderInterceptor', () => {
let httpMock: HttpTestingController;
let logService: LogService;
let httpClient: HttpClient;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ HttpClientTestingModule ],
providers: [
LogService,
{ provide: HTTP_INTERCEPTORS, useClass: LoggerInterceptor, multi: true }
]
});
httpMock = TestBed.get(HttpTestingController);
logService = TestBed.get(LogService);
httpClient = TestBed.get(HttpClient);
});
it('must log the http get request', () => {
const spy = spyOn(logService, 'log');
httpClient.get('http://jsonplaceholder.typicode.com/posts')
.subscribe(
(data: Array<any>) => {
expect(data.length).toBe(2);
}
);
const req: TestRequest = httpMock.expectOne('http://jsonplaceholder.typicode.com/posts');
expect(req.request.headers.has('Content-Type')).toBe(true);
expect(spy).toHaveBeenCalled();
req.flush(posts);
});
it('must log the http post request', () => {
const spy = spyOn(logService, 'log');
httpClient.post('http://jsonplaceholder.typicode.com/posts', posts)
.subscribe();
const req: TestRequest = httpMock.expectOne('http://jsonplaceholder.typicode.com/posts');
expect(req.request.headers.has('Content-Type')).toBe(true);
expect(spy).not.toHaveBeenCalled();
req.flush(posts);
});
});
I have HeaderInterceptor that adds Content-Type
header to each http request. Testing of that interceptor works fine.
When I tried to test the LoggerInterceptor, I get error on the spy expect(spy).toHaveBeenCalled();
Here's the error:
Error: Expected spy log to have been called.
at stack (http://localhost:9876/absolute/home/pritambohra/Desktop/testing-in-angular/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?0b1eaf7a13cae32191eadea482cfc96ae41fc22b:2455:17)
at buildExpectationResult (http://localhost:9876/absolute/home/pritambohra/Desktop/testing-in-angular/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?0b1eaf7a13cae32191eadea482cfc96ae41fc22b:2425:14)
at Spec.expectationResultFactory (http://localhost:9876/absolute/home/pritambohra/Desktop/testing-in-angular/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?0b1eaf7a13cae32191eadea482cfc96ae41fc22b:901:18)
at Spec.addExpectationResult (http://localhost:9876/absolute/home/pritambohra/Desktop/testing-in-angular/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?0b1eaf7a13cae32191eadea482cfc96ae41fc22b:524:34)
at Expectation.addExpectationResult (http://localhost:9876/absolute/home/pritambohra/Desktop/testing-in-angular/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?0b1eaf7a13cae32191eadea482cfc96ae41fc22b:845:21)
at Expectation.toHaveBeenCalled (http://localhost:9876/absolute/home/pritambohra/Desktop/testing-in-angular/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?0b1eaf7a13cae32191eadea482cfc96ae41fc22b:2369:12)
at UserContext.<anonymous> (http://localhost:9876/src/app/logger.interceptor.spec.ts?:57:17)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/node_modules/zone.js/dist/zone.js?:388:1)
at ProxyZoneSpec.push../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke (http://localhost:9876/node_modules/zone.js/dist/zone-testing.js?:288:1)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/node_modules/zone.js/dist/zone.js?:387:1)
Not really sure where I am going wrong. I am executing the post http call for the sake of code-coverage. What do I need to fix?
回答1:
This might only be a partial answer.
I am not expert in testing, so I shouldn't try to explain why your code failing, but I believe the code below is correct and doing true tests.
IMO there are two problems in your code in the GET case:
- you are flushing after expecting the spy - should be the other way around
- you are not calling done()
from within .subscribe()
Your code for POST case seems to work ok though (except that the test description seems incorrect, shouldn't it be "should NOT log" instead?)
I have left out the check of Content-Type
header as it was failing for some reason (don't have time to investigate it further - will be curious to see if you solve it) and it was not part of your question.
describe('Logger Interceptor', () => {
let httpMock: HttpTestingController;
let httpClient: HttpClient;
let logService: LogService;
let spy: Function;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: LoggerInterceptor,
multi: true,
},
],
});
httpClient = TestBed.get(HttpClient);
httpMock = TestBed.get(HttpTestingController);
logService = TestBed.get(LogService);
spy = spyOn(logService, 'log');
});
afterEach(() => httpMock.verify());
it('must log the http get request', (done) => {
httpClient.get('http://jsonplaceholder.typicode.com/posts').subscribe((data: any[]) => {
expect(data.length).toBe(2);
done();
});
const req: TestRequest = httpMock.expectOne('http://jsonplaceholder.typicode.com/posts');
req.flush(posts);
expect(spy).toHaveBeenCalled();
});
it('should NOT log the http post request', () => {
httpClient.post('http://jsonplaceholder.typicode.com/posts', posts)
.subscribe();
const req: TestRequest = httpMock.expectOne('http://jsonplaceholder.typicode.com/posts');
expect(spy).not.toHaveBeenCalled();
req.flush(posts);
});
});
I would be curious to hear why the GET case requires the above changes, should anyone be able to explain.
来源:https://stackoverflow.com/questions/53385444/spyon-not-working-in-http-interceptor-in-angular-6