如何在测试中模拟 Angular 4.3 httpClient 的错误响应
Posted
技术标签:
【中文标题】如何在测试中模拟 Angular 4.3 httpClient 的错误响应【英文标题】:How to mock Angular 4.3 httpClient an error response in testing 【发布时间】:2018-02-12 04:55:08 【问题描述】:我有一个下面的interceptor auth-interceptor.service.ts
import Injectable, Injector from '@angular/core';
import HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest from '@angular/common/http';
import Observable from 'rxjs/Observable';
import Cookie from './cookie.service';
import Router from '@angular/router';
import UserService from './user.service';
import ToasterService from '../toaster/toaster.service';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
@Injectable()
export class AuthInterceptor implements HttpInterceptor
constructor(private injector: Injector)
private handleError(err: HttpErrorResponse): Observable<any>
let errorMsg;
if (err.error instanceof Error)
// A client-side or network error occurred. Handle it accordingly.
errorMsg = `An error occurred: $err.error.message`;
else
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
errorMsg = `Backend returned code $err.status, body was: $err.error`;
if (err.status === 401 || err.status === 403)
this.injector.get(UserService).purgeAuth();
this.injector.get(ToasterService).showError(`Unauthorized`, errorMsg);
this.injector.get(Router).navigateByUrl(`/login`);
console.error(errorMsg);
return Observable.throw(errorMsg);
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>
// Clone the request to add the new header.
const authReq = req.clone(headers: req.headers.set(Cookie.tokenKey, Cookie.getToken()));
// Pass on the cloned request instead of the original request.
return next.handle(authReq).catch(err => this.handleError(err));
现在我正在尝试模拟 http.get
来抛出错误,以便方法 handleError
控制错误消息。
以下是我对测试用例的处理方法auth-interceptor.service.specs.ts
import async, inject, TestBed from '@angular/core/testing';
import AuthInterceptor from './auth-interceptor.service';
import ApiService from './api.service';
import HttpClientTestingModule, HttpTestingController from '@angular/common/http/testing';
import environment from '../../../environments/environment';
describe(`AuthInterceptor`, () =>
const somePath = `/somePath`;
beforeEach(() =>
TestBed.configureTestingModule(
imports: [HttpClientTestingModule],
providers: [AuthInterceptor, ApiService]
);
);
it(`should be created`, inject([AuthInterceptor], (service: AuthInterceptor) =>
expect(service).toBeTruthy();
));
it(`should log an error to the console on error on get()`, async(inject([ApiService, HttpTestingController],
(apiService: ApiService, httpMock: HttpTestingController) =>
spyOn(console, 'error');
apiService.get(somePath).subscribe((res) =>
console.log(`in success:`, res);
, (error) =>
console.log(`in error:`, error);
);
const req = httpMock.expectOne(`$environment.apiUri$somePath`);
req.flush(
type: 'ERROR',
status: 404,
body: JSON.stringify(color: `blue`)
);
expect(console.error).toHaveBeenCalled();
))
);
);
在刷新响应时,我不确定如何刷新错误响应,以便在我的拦截器中调用方法 handleError
并最终调用 console.error
。文档对我的情况没有任何示例。任何帮助或建议表示赞赏。
【问题讨论】:
您的req
对象是TestRequest
类的一个实例。它也有error()
方法。你试过req.error(new ErrorEvent('fail'), status: 404);
之类的吗?
【参考方案1】:
HttpTestingController
类中的expectOne
方法返回一个TestRequest
对象。这个TestRequest 类有一个flush
方法,可以用来传递
成功和不成功的响应。
我们可以通过返回正文以及一些额外的响应标头(如果有)来解决请求。相关信息可以在here找到。
现在,回到你如何做到这一点。您可以根据自己的用例自定义以下代码 sn-p。
http = TestBed.get(HttpTestingController);
let response: any;
let errResponse: any;
const mockErrorResponse = status: 400, statusText: 'Bad Request' ;
const data = 'Invalid request parameters';
apiService.get(somePath).subscribe(res => response = res, err => errResponse = err);
http.expectOne('url/being/monitored').flush(data, mockErrorResponse);
expect(errResponse).toBe(data);
注意:在撰写此评论时,mockErrorResponse
中需要 statusText
。相关信息可以在here找到。
P.S.:TestRequest
类的 error
方法可用于在我们的测试用例中模拟网络错误,因为它需要一个 Error 实例。下面的代码 sn -p 说明了这一点。
http.expectOne(someUrl).error(new ErrorEvent('network error'));
【讨论】:
如果检查错误,你不应该改为http.expectOne(someUrl).error(null, mockErrorResponse)
吗?创建ErrorEvent
不允许您包含状态代码,因此对我而言,它似乎不如我建议的解决方案那么理想。
我正在为此苦苦挣扎。如果我有一个茉莉花单元测试这样做它可以工作。如果我有多个,它实际上是在抛出我试图模拟的错误并终止测试周期。
@Jimbo 我遇到了同样的问题。我的问题是我没有添加错误回调。 apiService.get(somePath).subscribe(res => response = res, err => errResponse = err);
第二个回调非常重要,它甚至可以是像(err) =>
这样的空函数。以上是关于如何在测试中模拟 Angular 4.3 httpClient 的错误响应的主要内容,如果未能解决你的问题,请参考以下文章
Angular 6 - 如何在单元测试中模拟 router.events url 响应