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

Mar*_*ehl 7 testing rest http karma-runner angular

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

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

发生了什么:测试失败并显示错误消息:“预期的 data.forEach 不是包含‘404’的函数”

因此,服务获取 HttpErrorResponse 作为输入,但尝试将其解析为 map 函数中的常规响应。这失败了, catchError 被调用并且 data.forEach is not a function Error 被抛出。

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

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

if (data instanceof HttpErrorResponse)
      throw new HttpErrorResponse(data);
Run Code Online (Sandbox Code Playgroud)

考试:

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

服务:

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

我有什么问题吗?我认为预期的行为是我应该经历的,至少这就是我解释 Angular 文档的方式。

Ant*_*ony 10

迟到的回答,但可能会帮助面临类似问题的人。

根本原因

错误消息:“expected data.forEach is not a function to contains '404'”是因为of测试用例中的运算符:

httpClientSpy.get.and.returnValue(of(errorResponse));
Run Code Online (Sandbox Code Playgroud)

of运算符返回一个发出参数的可观察对象。

当您想要返回数据时,这很有用,但当您想要引发 404 错误时,这就没用了。

为了让间谍提出错误,响应应该拒绝而不是解析。

解决方案1

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

httpClientSpy.get.and.returnValue(of(errorResponse));
Run Code Online (Sandbox Code Playgroud)

解决方案2

最好使用 Angular HttpClientTestingModule来测试HttpClient使用情况。以下示例显示了使用的相同测试HttpClientTestingModule

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

Angular HTTP 测试文档解释了这种方法。

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


小智 5

一个迟到的答案,一种稍微不同的方式,但这也有效。

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

  • 搜索了几个小时,找到了这个答案,太棒了!谢谢。 (2认同)