angular-share运算符导致Jest测试失败
我有一个发出HTTP请求的Angular服务.该服务的主要工作是刷新访问令牌&如果请求导致401,则重试该请求.该服务还能够宽限期地处理多个并发请求:如果有3个请求导致401,则该令牌将仅刷新一次,并且将重放所有3个请求.
以下GIF总结了此行为: 我的问题是我似乎无法测试这种行为.最初,我的测试总是因超时而失败,因为我没有调用我的订阅或错误方法.添加fakeAsync之后我再也没有得到超时,但我的观察者仍然没有被调用.我还注意到,如果我从tokenObservable中删除共享运算符,则会调用我的测试中的订阅,但通过这样做,我将失去多播的好处. 这是不能正常工作的测试 it('refreshes token when getting a 401 but gives up after 3 tries',fakeAsync(() => { const errorObs = new Observable(obs => { obs.error({ status: 401 }); }).pipe( tap(data => { console.log('token refreshed'); }) ); const HttpClientMock = jest.fn<HttpClient>(() => ({ post: jest.fn().mockImplementation(() => { return errorObs; }) })); const httpClient = new HttpClientMock(); const tokenObs = new Observable(obs => { obs.next({ someProperty: 'someValue' }); obs.complete(); }); const AuthenticationServiceMock = jest.fn<AuthenticationService>(() => ({ refresh: jest.fn().mockImplementation(() => { return tokenObs; }) })); const authenticationService = new AuthenticationServiceMock(); const service = createSut(authenticationService,httpClient); service.post('controller',{}).subscribe( data => { expect(true).toBeFalsy(); },(error: any) => { expect(error).toBe('random string that is expected to fail the test,but it does not'); expect(authenticationService.refresh).toHaveBeenCalledTimes(3); } ); })); 这就是我在SUT中注入模拟的方法: const createSut = ( authenticationServiceMock: AuthenticationService,httpClientMock: HttpClient ): RefreshableHttpService => { const config = { endpoint: 'http://localhost:64104',login: 'token' }; const authConfig = new AuthConfig(); TestBed.configureTestingModule({ providers: [ { provide: HTTP_CONFIG,useValue: config },{ provide: AUTH_CONFIG,useValue: authConfig },{ provide: STATIC_HEADERS,useValue: new DefaultStaticHeaderService() },{ provide: AuthenticationService,useValue: authenticationServiceMock },{ provide: HttpClient,useValue: httpClientMock },RefreshableHttpService ] }); try { const testbed = getTestBed(); return testbed.get(RefreshableHttpService); } catch (e) { console.error(e); } }; 以下是被测系统的相关代码: @Injectable() export class RefreshableHttpService extends HttpService { private tokenObservable = defer(() => this.authenthicationService.refresh()).pipe(share()); constructor( http: HttpClient,private authenthicationService: AuthenticationService,injector: Injector ) { super(http,injector); } public post<T extends Response | boolean | string | Array<T> | Object>( url: string,body: any,options?: { type?: { new (): Response }; overrideEndpoint?: string; headers?: { [header: string]: string | string[] }; params?: HttpParams | { [param: string]: string | string[] }; } ): Observable<T> { return defer<T>(() => { return super.post<T>(url,body,options); }).pipe( retryWhen((error: Observable<any>) => { return this.refresh(error); }) ); } private refresh(obs: Observable<ErrorResponse>): Observable<any> { return obs.pipe( mergeMap((x: ErrorResponse) => { if (x.status === 401) { return of(x); } return throwError(x); }),mergeScan((acc,value) => { const cur = acc + 1; if (cur === 4) { return throwError(value); } return of(cur); },0),mergeMap(c => { if (c === 4) { return throwError('Retried too many times'); } return this.tokenObservable; }) ); } } 它继承的类: @Injectable() export class HttpService { protected httpConfig: HttpConfig; private staticHeaderService: StaticHeaderService; constructor(protected http: HttpClient,private injector: Injector) { this.httpConfig = this.injector.get(HTTP_CONFIG); this.staticHeaderService = this.injector.get(STATIC_HEADERS); } 由于某些未知原因,它不会在第二次调用时解析refresh方法返回的observable. 依赖关系和版本: > Angular 6.1.0 以下文章帮助我实现了这个功能: 解决方法
我已经调查了这个,似乎我有一些想法,为什么它不适合你:
1)Angular HttpClient服务在异步代码中引发错误,但您是同步执行的.结果它打破了共享操作符.如果您可以调试,可以通过查看ConnectableObservable.ts来查看问题 在您的测试连接中,当HttpClient异步代码中的连接取消订阅并关闭时,它仍将处于打开状态,以便下次创建新连接时. 要解决此问题,您还可以在异步代码中触发401错误: const errorObs = new Observable(obs => { setTimeout(() => { obs.error({ status: 404 }); }); ... 但是你必须等待使用tick执行所有异步代码: service.post('controller',(error: any) => { expect(error).toBe('Retried too many times'); expect(authenticationService.refresh).toHaveBeenCalledTimes(3); } ); tick(); // <=== add this 2)您应该在RefreshableHttpService中删除以下表达式: mergeScan((acc,value) => { const cur = acc + 1; if (cur === 4) { <== this one return throwError(value); } 因为我们不想在值上下文中抛出错误. 之后,您应该捕获所有刷新调用. 我还创建了示例项目https://github.com/alexzuza/angular-cli-jest 试试npm i和npm t. Share operator causes Jest test to fail √ refreshes token when getting a 401 but gives up after 3 tries (41ms) console.log src/app/sub/service.spec.ts:34 refreshing... console.log src/app/sub/service.spec.ts:34 refreshing... console.log src/app/sub/service.spec.ts:34 refreshing... Test Suites: 1 passed,1 total Tests: 1 passed,1 total Snapshots: 0 total Time: 4.531s,estimated 5s Ran all test suites. 您也可以通过npm run debug调试它 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |