单元测试时出错:“未捕获(承诺)语法错误:JSON中位置1的意外令牌o"; [英] Error when unit testing: "Uncaught (in promise) SyntaxError: Unexpected token o in JSON at position 1"

查看:124
本文介绍了单元测试时出错:“未捕获(承诺)语法错误:JSON中位置1的意外令牌o";的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

未捕获(承诺)SyntaxError:JSON中的意外令牌o位于 位置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.

NB:这篇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.

在获取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'.

只要Angular应用中未使用Fetch API,这是理想的行为.

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.

这篇关于单元测试时出错:“未捕获(承诺)语法错误:JSON中位置1的意外令牌o";的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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