单元测试时出错:"未捕获(在promise中)SyntaxError:在位置1的JSON中出现意外的标记o"

til*_*ilo 6 json unit-testing mocking angular

我想对服务进行单元测试,但是,在运行测试时,我收到以下错误:

未捕获(在promise中)SyntaxError:MapSubscriber.Array中MapSubscriber.Array.concat.MapSubscriber._next(map.js:77)位于MapSubscriber.project(auth.service.ts:217)的位置1的JSON中的意外标记o. concat.Subscriber.next(Subscriber.js:89)在TakeSubscriber.Array.concat.TakeSubscriber._next(take.js:80),位于ReplaySubject.Array的TakeSubscriber.Array.concat.Subscriber.next(Subscriber.js:89) ReplaySubject上的ReplaySubject.Array.concat.Orray.Array.concat.Atry.Subject.Array.concat.Atry.Subject._trySubscribe(Subject.js:97)中的ReplaySubject.Array.concat.Observable._trySubscribe(Observable.js:57).concat.ReplaySubject._subscribe(ReplaySubject.js:55).位于MapOperator的AnonymousSubject.Array.concat.Observable.subscribe(Observable.js:42)的TakeOperator.Array.concat.TakeOperator.call(take.js:60)中的Array.concat.Observable.subscribe(Observable.js:45) .Array.concat.MapOperator.call(map.js:54)位于CatchOperator.Array.concat.CatchOperator.call(catch.js:79)的AnonymousSubject.Array.concat.Observable.subscribe(Observable.js:42)一个 nonymousSubject.Array.concat.Observable.subscribe(Observable.js:42)

相应的行(auth.service.ts:217)在代码中突出显示.运行应用程序完全正常,因此我没有看到测试失败的明显原因.

注意:这个SO帖子表明我正在解析对象两次.但是,当运行应用程序时,它不应该失败吗?

auth.service.ts

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);
  }
Run Code Online (Sandbox Code Playgroud)

auth.service.spec.ts

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');
    });
  }));

});
Run Code Online (Sandbox Code Playgroud)

记录响应输出如下:

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
Run Code Online (Sandbox Code Playgroud)

Est*_*ask 17

该错误Unexpected token ... in JSON at position 1实际上意味着JSON.parse应用于无效JSON的东西 - 一个随机字符串或一个完全不是字符串的字符串被强制转换为字符串.

该消息Unexpected token o ...暗示解析的值很可能是一个对象 - 它被强制转换为[object ...]字符串.

问题在这里清楚地看到:

Response
  body: ReadableStream
    locked: true
    __proto__: Object
  bodyUsed: true
  ...
Run Code Online (Sandbox Code Playgroud)

这里的响应对象是全局Response构造函数的实例(Fetch API的一部分),并且存在ReadableStream清楚地表明了这一点.

正如在Fetch polyfill中可以看到的那样,所有这一切res.json()都适用JSON.parse于某些值

this.json = function() {
  return this.text().then(JSON.parse)
}
Run Code Online (Sandbox Code Playgroud)

这可能new ResponseOptions是错误地提供给全局Response构造函数的对象,因此是错误.

Angular类似地命名了Response,它从Fetch派生它的接口,Response但显然不兼容.问题是它从未被导入,因此使用了全局Response.

怎么修

它应该是

import { Response, ResponseOptions, ... } from '@angular/http';
Run Code Online (Sandbox Code Playgroud)

如何预防

Globals在Typescript类型定义中声明,它们不能是未声明的,但可以在自定义类型定义中重新声明:

custom.d.ts

declare var Headers: undefined;
declare var Request: undefined;
declare var Response: undefined;
declare var URLSearchParams: undefined;
Run Code Online (Sandbox Code Playgroud)

使用全局变量而不是同名的导入的Http类将导致类型错误:

TS2532:对象可能是"未定义的".

只要在Angular应用程序中不使用Fetch API,这是一种理想的行为.

这不是Angular 4.3 HttpClient中替换的问题,Http也不使用与Fetch API中的类相同的类.