单元测试时出错:“Uncaught (in promise) SyntaxError: Unexpected token o in JSON at position 1" [英] Error when unit testing: "Uncaught (in promise) SyntaxError: Unexpected token o in JSON at position 1"

查看:171
本文介绍了单元测试时出错:“Uncaught (in promise) SyntaxError: Unexpected token o in JSON at position 1"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'd like to unit test a service, however, when running the test, I get the following error:

Uncaught (in promise) SyntaxError: Unexpected token o in JSON at位置 1在 MapSubscriber.project (auth.service.ts:217)在 MapSubscriber.Array.concat.MapSubscriber._next (map.js:77)在 MapSubscriber.Array.concat.Subscriber.next (Subscriber.js:89)在 TakeSubscriber.Array.concat.TakeSubscriber._next (take.js:80)在 TakeSubscriber.Array.concat.Subscriber.next (Subscriber.js:89)在 ReplaySubject.Array.concat.ReplaySubject._subscribe (ReplaySubject.js:55)在 ReplaySubject.Array.concat.Observable._trySubscribe (Observable.js:57)在 ReplaySubject.Array.concat.Subject._trySubscribe (Subject.js:97)在 ReplaySubject.Array.concat.Observable.subscribe (Observable.js:45)在 TakeOperator.Array.concat.TakeOperator.call (take.js:60)在 AnonymousSubject.Array.concat.Observable.subscribe (Observable.js:42)在 MapOperator.Array.concat.MapOperator.call (map.js:54)在 AnonymousSubject.Array.concat.Observable.subscribe (Observable.js:42)在 CatchOperator.Array.concat.CatchOperator.call (catch.js:79)在 AnonymousSubject.Array.concat.Observable.subscribe (Observable.js:42)

Uncaught (in promise) SyntaxError: Unexpected token o in JSON at position 1 at MapSubscriber.project (auth.service.ts:217) at MapSubscriber.Array.concat.MapSubscriber._next (map.js:77) at MapSubscriber.Array.concat.Subscriber.next (Subscriber.js:89) at TakeSubscriber.Array.concat.TakeSubscriber._next (take.js:80) at TakeSubscriber.Array.concat.Subscriber.next (Subscriber.js:89) at ReplaySubject.Array.concat.ReplaySubject._subscribe (ReplaySubject.js:55) at ReplaySubject.Array.concat.Observable._trySubscribe (Observable.js:57) at ReplaySubject.Array.concat.Subject._trySubscribe (Subject.js:97) at ReplaySubject.Array.concat.Observable.subscribe (Observable.js:45) at TakeOperator.Array.concat.TakeOperator.call (take.js:60) at AnonymousSubject.Array.concat.Observable.subscribe (Observable.js:42) at MapOperator.Array.concat.MapOperator.call (map.js:54) at AnonymousSubject.Array.concat.Observable.subscribe (Observable.js:42) at CatchOperator.Array.concat.CatchOperator.call (catch.js:79) at AnonymousSubject.Array.concat.Observable.subscribe (Observable.js:42)

相应的行 (auth.service.ts:217) 在下面的代码中突出显示.运行应用程序运行良好,因此我看不出测试失败的明显原因.

The corresponding line (auth.service.ts:217) is highlighted below in code. Running the application works perfectly fine, therefore I do not see an obvious reason for the test to fail.

注意:这篇 SO 帖子建议我m 解析对象两次.但是在运行应用程序时它不应该也失败吗?

NB: This SO post suggests that I'm parsing the object twice. But shouldn't it fail when running the application then, too?

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

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

});

记录响应输出以下内容:

Logging the response outputs the following:

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 的内容 - 一个随机字符串或一个根本不是字符串的值,它被强制转换为字符串.

The error Unexpected token ... in JSON at position 1 actually means that JSON.parse was applied to something that is not valid JSON - a random string or a value that is not a string at all which was coerced to string.

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

The message Unexpected token o ... implies that parsed value was most likely an object - which is coerced to [object ...] string.

问题在这里很明显:

Response
  body: ReadableStream
    locked: true
    __proto__: Object
  bodyUsed: true
  ...

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

Response object here is an instance of global Response constructor (a part of Fetch API), and the presence of ReadableStream clearly indicates this.

在 Fetch polyfill 中可以看到,所有 res.json() 所做的只是将 JSON.parse 应用于某个值

As it can be seen in Fetch polyfill, all that res.json() does is applying JSON.parse to some value

this.json = function() {
  return this.text().then(JSON.parse)
}

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

which is likely new ResponseOptions object that was erroneously supplied to global Response constructor, hence the error.

Angular 也有类似的名称 Response 从 Fetch Response 派生其接口,但显然不兼容.问题是它从未被导入,因此改为使用全局 Response.

Angular has similarly named Response class which derives its interface from Fetch Response but obviously isn't compatible. The problem is that it was never imported and thus global Response was used instead.

应该是

import { Response, ResponseOptions, ... } from '@angular/http';

如何预防

全局变量在 Typescript 类型定义中声明,它们不能未声明,但可以在自定义类型定义中重新声明:

How to prevent

Globals are declared in Typescript type definitions, they cannot be undeclared but can be re-declared in custom type definition:

custom.d.ts

declare var Headers: undefined;
declare var Request: undefined;
declare var Response: undefined;
declare var URLSearchParams: undefined;

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

Using globals instead of imported Http classes of the same name will result in type error:

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

TS2532: Object is possibly 'undefined'.

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

Which is a desirable behaviour as long as Fetch API isn't used in Angular app.

这不是 HttpClient 的问题,它在 Angular 4.3 中替换了 Http 并且不使用与 Fetch API 中的类同名的类.

This is not an issue for HttpClient that replaced Http in Angular 4.3 and doesn't use classes of same names as the ones from Fetch API.

这篇关于单元测试时出错:“Uncaught (in promise) SyntaxError: Unexpected token o in JSON at position 1"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆