模拟 Observable 以在 Jest 中抛出错误
Posted
技术标签:
【中文标题】模拟 Observable 以在 Jest 中抛出错误【英文标题】:Mocking Observable to throw error in Jest 【发布时间】:2020-09-27 22:05:10 【问题描述】:我正在尝试模拟 Angular 的 HttpClient
的 PUT
调用以引发错误。我正在使用throwError
。它不工作。我应该改变什么让它抛出错误并调用handleError
方法? 我正在使用 Jest。
it(`should call the 'handleError' method when a request to store data was not successful`, () =>
const error: HttpErrorResponse =
status: 401,
message: 'You are not logged in',
as HttpErrorResponse;
jest.spyOn(httpClientServiceMock, 'put').mockReturnValue(throwError(error));
const spy = jest.spyOn(httpService, 'handleError');
httpService.requestCall('some-url', ApiMethod.PUT, );
expect(spy).toBeCalled();
);
服务文件
requestCall(url: string, method: ApiMethod, data?: any): Observable<any>
const headers =
'X-XSRF-TOKEN': this.xsrfToken,
'Content-Type': 'application/json; charset=UTF-8',
;
const requestConfig =
withCredentials: true,
headers,
;
switch (method)
case ApiMethod.GET:
return this._http.get(url, withCredentials: true );
case ApiMethod.PUT:
return this._http
.put(url, data, requestConfig)
.pipe(catchError((error) => this.handleError(error)));
handleError(error: HttpErrorResponse): any
if (error.error instanceof ErrorEvent)
console.error(`An error occurred: $error.error.message`);
return throwError( error: error.message, status: error.status );
【问题讨论】:
【参考方案1】:你已经很接近了!
你必须从httpService.requestCall('some-url', ApiMethod.PUT, )
函数返回subscribe to observable。 There are additional changes required as this is asynchronous
const of , throwError, operators:
catchError
= rxjs;
const httpClientServiceMock =
put: () => of (
value: 'test'
)
;
const httpService =
requestCall(url, data, requestConfig)
return httpClientServiceMock
.put(url, data, requestConfig)
.pipe(catchError((error) => this.handleError(error)));
,
handleError(error)
return throwError();
;
const ApiMethod =
PUT: ''
const
expect,
test,
run,
it,
describe,
jest
= jestLite.core;
describe('httpService', () =>
it(`should call the 'handleError' method when a request to store data was not successful`, done =>
const error =
status: 401,
message: 'You are not logged in',
jest.spyOn(httpClientServiceMock, 'put').mockReturnValue(throwError(error));
const spy = jest.spyOn(httpService, 'handleError');
httpService
.requestCall('some-url', ApiMethod.PUT, )
.subscribe(pr =>
done.fail(new Error(`It shouldn't go this path!`))
, error =>
expect(spy).toBeCalled();
done();
);
);
);
run().then(result =>
console.log(result[0]);
)
<script src="https://cdn.jsdelivr.net/npm/jest-lite@1.0.0-alpha.4/dist/core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.js"></script>
【讨论】:
这就是为什么应该尽可能避免done
的原因,因为它会产生许多故障点。 expect(spy).toBeCalled(); done();
- 断言失败,从不调用 done 并导致超时,开发人员感到困惑。更好地使用 async
和 toPromise
进行 RxJS 测试,至少对于已完成的 observables。【参考方案2】:
正如在另一个答案中已经指出的那样,您必须订阅返回的 observable。
我只是想添加另一种使用 marble-testing 的方法,因此您不必手动订阅该 observable:
let testScheduler;
beforeEach(() => testScheduler = new TestScheduler(assertionFn))
it(`should call the 'handleError' method when a request to store data was not successful`, () =>
const error =
status: 401,
message: 'You are not logged in',
as HttpErrorResponse;
jest.spyOn(httpClientServiceMock, 'put').mockReturnValue(throwError(error));
const spy = jest.spyOn(httpService, 'handleError');
testScheduler.run(( cold, expectObservable ) =>
const src$ = httpService.requestCall('some-url', ApiMethod.PUT, );
expectObservable(src$).toBe('#', null, error: error.message, status: error.status );
expect(spy).toBeCalled();
);
);
TestScheduler
在rxjs/testing
中可用,run
的回调提供了几个帮助器,例如:cold
、hot
、flush
、expectObservable
、expectSubscriptions
和 time
.
我个人喜欢这一切的是一切都是同步的,所以在采用这种方法时,您可能不必调用done()
。
【讨论】:
以上是关于模拟 Observable 以在 Jest 中抛出错误的主要内容,如果未能解决你的问题,请参考以下文章