Angular 7-在单元测试中捕获HttpErrorResponse

时间:2018-12-04 14:52:50

标签: angular rest http testing karma-runner

我目前正在学习Angular 7(以前没有使用任何以前的版本),并且在编写服务的单元测试时遇到了无法解决的问题。

我有一个从REST获取JSON并将其解析为Class的服务。 提到Angular Docs,我使用HttpClientSpy编写了一个模拟404错误的测试。

会发生什么情况:测试失败并显示错误消息:“预期数据。forEach不是包含'404'的函数”

因此,该服务将HttpErrorResponse作为输入,但是尝试解析它,就像它是map函数中的常规响应一样。失败,将调用catchError,并且data.forEach不是函数会引发错误。

预期的行为:我希望不会执行map(),它应该直接跳到catchError函数中。

我如何修复(目前):将以下代码行添加到服务的地图功能使测试正常进行。

if (data instanceof HttpErrorResponse)
      throw new HttpErrorResponse(data);

测试:

it('should throw an error when 404', () => {

const errorResponse = new HttpErrorResponse({
  error: '404 error',
  status: 404, statusText: 'Not Found'
});

httpClientSpy.get.and.returnValue(of(errorResponse));

service.getComments().subscribe(
  fail,
  error => expect(error.message).toContain('404')
);
});

服务:

getComments(): Observable<CommentList> {
return this.http
.get('https://jsonplaceholder.typicode.com/comments')
.pipe(
  map((data: Array<any>) => {
    let t: Array<Comment> = [];

    data.forEach(comment => {

      if(!('id' in comment) || !('body' in comment) || !('email' in comment) || !('name' in comment))
        throw new Error("Could not cast Object returned from REST into comment");

      t.push(<Comment>{
        id: comment.id,
        body: comment.body,
        author: comment.email,
        title: comment.name,
      });

    });
    return new CommentList(t);
  }),
  catchError((err: HttpErrorResponse) => {
    return throwError(err);
  })
);
}

我弄错了吗?我认为预期的行为是我应该经历的,至少这就是我解释Angular文档的方式。

2 个答案:

答案 0 :(得分:1)

一个较晚的答案,稍有不同,但这也可以。

  it('should show modal if failed', inject([Router], (mockRouter: Router) => {
  const errorResponse = new HttpErrorResponse({
     error: { code: `some code`, message: `some message.` },
     status: 400,
     statusText: 'Bad Request',
  });

  spyOn(someService, 'methodFromService').and.returnValue(throwError(errorResponse));
  expect...
  expect...
  expect...
}));

答案 1 :(得分:0)

最新答案,但可能会帮助面临类似问题的人。

根本原因

错误消息:“预期的data.forEach不是包含'404'的函数”是由于在测试用例中使用了of运算符:

httpClientSpy.get.and.returnValue(of(errorResponse));

of运算符返回一个可观察变量,该变量发出参数。

这在您要返回数据时有用,而在您要引发404错误时则无用。

为了使间谍举报错误,响应应该拒绝而不是解决。

解决方案1 ​​

此解决方案使用defer RxJS运算符以及您在示例中使用的jasmine.createSpyObj方法。

import { TestBed } from '@angular/core/testing';
import { HttpErrorResponse } from '@angular/common/http';
import { defer } from 'rxjs';

import { CommentsService } from './comments.service';

// Create async observable error that errors
//  after a JS engine turn
export function asyncError<T>(errorObject: any) {
  return defer(() => Promise.reject(errorObject));
}

describe('CommentsService', () => {
  let httpClientSpy: { get: jasmine.Spy };
  let service: CommentsService;

  beforeEach(() => {
    httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']);
    service = new CommentsService(httpClientSpy as any);
  });

  it('should throw an error when 404', () => {
    const errorResponse = new HttpErrorResponse({
      error: '404 error',
      status: 404,
      statusText: 'Not Found'
    });

    httpClientSpy.get.and.returnValue(asyncError(errorResponse));

    service.getComments().subscribe(
      data => fail('Should have failed with 404 error'),
      (error: HttpErrorResponse) => {
        expect(error.status).toEqual(404);
        expect(error.error).toContain('404 error');
      });
  });
});

解决方案2

最好使用成角的HttpClientTestingModule来测试HttpClient的用法。以下示例显示了使用HttpClientTestingModule的相同测试。

import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { HttpErrorResponse } from '@angular/common/http';

import { CommentsService } from './comments.service';

describe('CommentsService test using HttpClientTestingModule', () => {
  let httpTestingController: HttpTestingController;
  let service: CommentsService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [ HttpClientTestingModule ]
    });

    httpTestingController = TestBed.get(HttpTestingController);
    service = TestBed.get(CommentsService);
  });

  it('throws 404 error', () => {
    service.getComments().subscribe(
      data => fail('Should have failed with 404 error'),
      (error: HttpErrorResponse) => {
        expect(error.status).toEqual(404);
        expect(error.error).toContain('404 error');
      }
    );

    const req = httpTestingController.expectOne('https://jsonplaceholder.typicode.com/comments');

    // Respond with mock error
    req.flush('404 error', { status: 404, statusText: 'Not Found' });
  });
});

有角度的HTTP Testing文档说明了这种方法。

注意:这些示例已使用Angular v8进行了测试。