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

时间:2017-04-25 14:00:51

标签: json unit-testing angular mocking

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

  

Uncaught(在promise中)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)       at AnonymousSubject.Array.concat.Observable.subscribe(Observable.js:42)

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

注意:This SO post表示我正在解析对象两次。但是,当运行应用程序时,它不应该失败吗?

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

});

记录响应输出如下:

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

4 个答案:

答案 0 :(得分:16)

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

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

这里可以清楚地看到问题:

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

此处的响应对象是全局Response constructor(Fetch API的一部分)的实例,ReadableStream的存在清楚地表明了这一点。

As it can be seen in Fetch polyfillres.json()所做的就是将JSON.parse应用于某个值

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

可能new ResponseOptions对象被错误地提供给全局Response构造函数,因此出错。

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

如何修复

应该是

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

如何预防

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

custom.d.ts

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

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

  

TS2532:对象可能未定义&#39;。

只要在Angular应用中未使用Fetch API,这是一种理想的行为。

HttpClient替换了Angular 4.3中的Http,并且没有使用与Fetch API 相同名称的类,这不是问题。< / p>

答案 1 :(得分:2)

  

SyntaxError:位置1的JSON中的意外标记o

错误的原因:

  • 您可能希望Content-Type作为JSON作为API的回复,但您的回复为String字母o的开头。因此,它会引发错误Unexpected token o in JSON at position 1
  • 如果响应标头是text/html,则需要解析,如果响应标头为application/json,则已经为您解析了。如果您再次解析它,它将为您提供unexpected token o

如果响应已经是JSON对象,则无需使用response.json()

答案 2 :(得分:0)

无需解析数据。

通常,当您遇到此错误时,表示您不需要解析数据。

尝试使用&#39;令牌&#39;直。

为了理智,请在控制台中打印数据。 (你也可以对它进行字符串化并打印出来,看看它给你的东西)

答案 3 :(得分:0)

事实证明我错过了Response的导入:

之前:import { BaseRequestOptions, Http, ResponseOptions } from '@angular/http';

之后:import { BaseRequestOptions, Http, Response, ResponseOptions } from '@angular/http';

不从Response导入@angular/httpResponse默认为ECMAScript API。