我想对服务进行单元测试,但是,在运行测试时,出现以下错误:
未捕获(承诺)SyntaxError:JSON在MapSubscriber.Array的MapSubscriber.Array.concat.MapSubscriber._next(map.js:77)的MapSubscriber.project(auth.service.ts:217)位置1处出现意外令牌o。在ReplaySubject.Array的TakeSubscriber.Array.concat.TakeSubscriber._next(take.js:80)在TakeSubscriber.Array.concat.Subscriber.next(Subscriber.js:89)的concat.Subscriber.next(Subscriber.js:89)在ReplaySubject的ReplaySubject.Array.concat.Observable._trySubscribe(Observable.js:57)在ReplaySubject.Array.concat.Subject._trySubscribe(Subject.js:97)处的.concat.ReplaySubject._subscribe(ReplaySubject.js:55)。 Array.concat.Observable.subscribe(Observable.js:45)位于AnonymousSubject.Array.concat.Observable的TakeOperator.Array.concat.TakeOperator.call(take.js:60)。在CatchOperator.Array.concat.CatchOperator处在MapOperator.Array.concat.MapOperator.call(map.js:54)在AnonymousSubject.Array.concat.Observable.subscribe(Observable.js:42)处进行订阅(Observable.js:42) .call(catch.js:79)位于AnonymousSubject.Array.concat.Observable.subscribe(Observable.js:42)
相应的行(auth.service.ts:217)在下面的代码中突出显示。运行该应用程序可以正常运行,因此我看不出测试失败的明显原因。
auth.service.ts:217
注意:这篇SO帖子建议我将对象解析两次。但是,当运行应用程序时,它也不应该失败吗?
验证服务
public login(username: string, password: string): Observable<User> { // ... return this.http.request(path, requestOptions).map((response: Response) => { if (response.status === 200) { const token = response.json().token; // <<-- Uncaught (in promise) SyntaxError: Unexpected token o in JSON at position 1 const user = this.extractUser(response); return user; } return null; }) .catch(this.handleError); }
认证服务规范
describe('AuthService', () => { beforeEach(() => { TestBed.configureTestingModule({ providers: [ AuthService, MockBackend, BaseRequestOptions, { provide: Http, useFactory: (backend: MockBackend, options: BaseRequestOptions) => new Http(backend, options), deps: [MockBackend, BaseRequestOptions] } ], imports: [ RouterTestingModule ], }); }); it('should return an Observable<User>', inject([AuthService, MockBackend], (authService: AuthService, mockBackend: MockBackend) => { mockBackend.connections.subscribe((connection: any) => { connection.mockRespond(new Response(new ResponseOptions({ body: '{"token": "abc123", "name":"Jeff"}' }))); }); authService.login('jeff@example.com', 'password').subscribe(user => { expect(user.name).toEqual('Jeff'); }); })); });
记录响应输出以下内容:
Response body: ReadableStream locked: true __proto__: Object bodyUsed: true headers: Headers __proto__: Headers ok: true redirected: false status: 200 statusText: "OK" type: "default" url: "" __proto__: Response
该错误Unexpected token ... in JSON at position 1实际上意味着已将JSON.parse其应用于无效的JSON-随机字符串或强制转换为字符串的值根本不是字符串的值。
Unexpected token ... in JSON at position 1
JSON.parse
该消息Unexpected token o ...暗示解析的值很可能是对象-被强制转换为[object ...]字符串。
Unexpected token o ...
[object ...]
问题在这里清楚可见:
Response body: ReadableStream locked: true __proto__: Object bodyUsed: true ...
此处的响应对象是全局Response构造函数的实例(Fetch API的一部分),而是否存在ReadableStream清楚地表明了这一点。
Response
ReadableStream
在Fetch polyfill中可以看到,所有这些res.json()都适用JSON.parse于某些值
res.json()
this.json = function() { return this.text().then(JSON.parse) }
这很可能new ResponseOptions是错误地提供给全局Response构造函数的对象,因此发生了错误。
new ResponseOptions
Angular具有类似名称的Response类,该类从Fetch派生其接口,Response但显然不兼容。问题在于它从未被导入过,因此使用了global Response来代替。
它应该是
import { Response, ResponseOptions, ... } from '@angular/http';
全局变量在Typescript类型定义中声明,不能取消声明,但可以在自定义类型定义中重新声明:
定制文件
declare var Headers: undefined; declare var Request: undefined; declare var Response: undefined; declare var URLSearchParams: undefined;
使用全局变量而不是导入的同名Http类将导致类型错误:
TS2532:对象可能是“未定义”。
只要在Angular应用程序中未使用Fetch API,这就是理想的行为。
这不是 在Angular 4.3 HttpClient中替换的问题,Http并且不使用与Fetch API相同名称的类。
HttpClient
Http