【问题标题】:Angular 11: Unit testing an HttpInterceptor - async timer or AfterAll errorsAngular 11:对 HttpInterceptor 进行单元测试 - 异步计时器或 AfterAll 错误
【发布时间】:2021-03-10 14:41:08
【问题描述】:

我正在努力让我的 HttpInterceptor 通过我的单元测试,但我遇到了让测试通过的问题。我有 6 个测试,如果我将测试作为“正常”测试运行,当测试在本地完成时,我经常会收到“在 afterAll 中引发错误”,但我的 Azure 管道每次都失败并出现相同的错误。

如果我使用 fakeAsync() 在异步区域中运行测试,每个测试都会收到“1 个计时器仍在队列中”错误。

这是拦截器的代码:

import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, throwError } from 'rxjs';
import { retry, tap, catchError } from 'rxjs/operators';
import { HttpServiceError } from '../models/httpServiceError.model';
import { LoggingService, LogLevel } from '../services/logging.service';

@Injectable({
    providedIn: 'root'
})

export class HttpResponseInterceptorService implements HttpInterceptor{
  public logLevel!: LogLevel;

  constructor(private readonly loggingService: LoggingService) {
// Intentionally left blank
  }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request)
      .pipe(
        retry(1),
        tap(() => console.log('ResponseInterceptor called')),
        catchError((error: HttpErrorResponse) => {
          this.handleHttpError(error);
          return throwError(error);
        }));
  }

  private handleHttpError(error: HttpErrorResponse): Observable<HttpServiceError> {
    const requestError = new HttpServiceError();    
    
    requestError.errorNumber = error.status;
    requestError.statusMessage = error.statusText;

    switch (error.status) {
      case 401: {
        requestError.friendlyMessage = 'You are not logged in';
        break;
      }
      case 403: {
        requestError.friendlyMessage = 'You are not logged in';
        break;
      }
      case 404: {
        requestError.friendlyMessage = 'The service failed to respond';
        break;
      }
      case 429: {
        requestError.friendlyMessage = 'The service is busy';
        break;
      }
      case 500: {
        requestError.friendlyMessage = 'The service is not responding correctly';
        break;
      }
      default: {
        requestError.friendlyMessage = 'An error occured retrieving the data';
        break;
      }
    }

    // TODO replace with some kind of notification
    // window.alert(errorMessage);

    this.loggingService.logWithLevel(JSON.stringify(requestError), LogLevel.Information);

    return throwError(requestError);
  }
}

这是我的规范文件:

import { HttpClient, HttpErrorResponse, HTTP_INTERCEPTORS } from '@angular/common/http';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { fakeAsync, flush, inject, TestBed } from '@angular/core/testing';
import { LoggingService } from '../services/logging.service';

import { HttpResponseInterceptorService } from './httpresponseinterceptor.service';

describe('HttpResponseInterceptorService', () => {
  let interceptor: HttpResponseInterceptorService;
  let httpMock: HttpTestingController;
  let loggingService: LoggingService;
  let httpClient: HttpClient;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [
        {
          provide: HTTP_INTERCEPTORS,
          useClass: HttpResponseInterceptorService,
          multi: true,
        },
      ]
    });
    loggingService = TestBed.inject(LoggingService);
    interceptor = TestBed.inject(HttpResponseInterceptorService);
    httpMock = TestBed.inject(HttpTestingController);
    httpClient = TestBed.inject(HttpClient);
  });

  afterEach(() => {
    httpMock.verify();
  });

  afterAll(() => {
    TestBed.resetTestingModule();
  });

  it('should be created', () => {
    expect(interceptor).toBeTruthy();
  });

  it('should call loggingService with a friendlyMessage of An error occured retrieving the data (everything else)', fakeAsync(() => {
    spyOn(loggingService,'logWithLevel');

    const errorResponse = new HttpErrorResponse({
      error: '599 error',
      status: 599, statusText: 'Unknown Error'
    });

    httpClient.get('/api').subscribe();

    let request = httpMock.expectOne('/api');
    request.error(new ErrorEvent('599 First Unknown Error'), errorResponse);

    request = httpMock.expectOne('/api');
    request.error(new ErrorEvent('599 Second Unknown Error'), errorResponse);

    expect(loggingService.logWithLevel).toHaveBeenCalled();
  }));

  it('should call loggingService with a friendlyMessage of You are not logged in (401)', fakeAsync(() => {
    spyOn(loggingService,'logWithLevel');

    const errorResponse = new HttpErrorResponse({
      error: '401 error',
      status: 401, statusText: 'Unauthorized'
    });

    httpClient.get('/api').subscribe();

    let request = httpMock.expectOne('/api');
    request.error(new ErrorEvent('401 First Unauthorized'), errorResponse);

    request = httpMock.expectOne('/api');
    request.error(new ErrorEvent('401 Second Unauthorized'), errorResponse);

    expect(loggingService.logWithLevel).toHaveBeenCalled();
  }));

  it('should call loggingService with a friendlyMessage of You are not logged in (403)', fakeAsync(() => {
    spyOn(loggingService,'logWithLevel');

    const errorResponse = new HttpErrorResponse({
      error: '403 error',
      status: 403, statusText: 'Forbidden'
    });

    httpClient.get('/api').subscribe();

    let request = httpMock.expectOne('/api');
    request.error(new ErrorEvent('403 First Forbidden'), errorResponse);

    request = httpMock.expectOne('/api');
    request.error(new ErrorEvent('403 Second Forbidden'), errorResponse);

    expect(loggingService.logWithLevel).toHaveBeenCalled();
  }));

  it('should call loggingService with a friendlyMessage of The service failed to respond (404)', fakeAsync(() => {
    spyOn(loggingService,'logWithLevel');

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

    httpClient.get('/api').subscribe();

    let request = httpMock.expectOne('/api');
    request.error(new ErrorEvent('404 First Error'), errorResponse);

    request = httpMock.expectOne('/api');
    request.error(new ErrorEvent('404 Second Error'), errorResponse);

    expect(loggingService.logWithLevel).toHaveBeenCalled();
  }));

  it('should call loggingService with a friendlyMessage of The service is busy (429)', fakeAsync(() => {
    spyOn(loggingService,'logWithLevel');

    const errorResponse = new HttpErrorResponse({
      error: '429 error',
      status: 429, statusText: 'Too Many Requests'
    });

    httpClient.get('/api').subscribe();

    let request = httpMock.expectOne('/api');
    request.error(new ErrorEvent('429 First Too Many Requests'), errorResponse);

    request = httpMock.expectOne('/api');
    request.error(new ErrorEvent('429 Second Too Many Requests'), errorResponse);

    expect(loggingService.logWithLevel).toHaveBeenCalled();

  }));

  it('should call loggingService with a friendlyMessage of The service is not responding correctly (500)', fakeAsync(() => {
    spyOn(loggingService,'logWithLevel');

    const errorResponse = new HttpErrorResponse({
      error: '500 error',
      status: 500, statusText: 'Internal Server Error'
    });

    httpClient.get('/api').subscribe();

    let request = httpMock.expectOne('/api');
    request.error(new ErrorEvent('500 First Internal Server Error'), errorResponse);

    request = httpMock.expectOne('/api');
    request.error(new ErrorEvent('500 Second Internal Server Error'), errorResponse);

    expect(loggingService.logWithLevel).toHaveBeenCalled();
  }));

});

似乎有一个未完成的异步调用。我在httpClient.get() 调用之后尝试使用flush(),我尝试在每次调用之后将它放在request = httpMock.expectOne('/api') 之后,但似乎没有任何东西可以真正修复错误。

这也可能是因为我笨拙的按条件进行一次测试的方法。

非常欢迎好的想法和反馈!

谢谢,比亚恩

【问题讨论】:

    标签: angular unit-testing karma-jasmine


    【解决方案1】:

    因此,在对HttpTestingController 进行了一些研究之后,我能够重新创建并重现该错误。解决方案与我之前的答案相差不远,但根本不正确。

    subscribe() 之后缺少tick(200),但是当您返回错误时,您必须处理订阅中的错误。

    所以这里有一个第一个测试的工作示例。

    fit("should call loggingService with a friendlyMessage of An error occured retrieving the data (everything else)", fakeAsync(() => {
        spyOn(loggingService, "logWithLevel");
    
        const errorResponse = new HttpErrorResponse({
          error: "599 error",
          status: 599,
          statusText: "Unknown Error"
        });
    
        httpClient
          .get("/api")
          .subscribe(_ => console.log("Good"), err => console.log(err));
    
        let request = httpMock.expectOne("/api");
        request.error(new ErrorEvent("599 First Unknown Error"), errorResponse);
    
        request = httpMock.expectOne("/api");
        request.error(new ErrorEvent("599 Second Unknown Error"), errorResponse);
    
        tick(100);
        expect(loggingService.logWithLevel).toHaveBeenCalled();
      }));
    

    【讨论】:

    • 我刚开始测试您的建议。你完全搞定了!谢谢!
    猜你喜欢
    • 2021-05-02
    • 1970-01-01
    • 1970-01-01
    • 2020-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-09-29
    相关资源
    最近更新 更多