测试角度HTTP拦截器-预期在拦截返回之前调用

时间:2019-12-18 01:20:55

标签: angular rxjs angular-http-interceptors

我有一个简单的HttpInterceptor用于设置加载状态。这是代码

@Injectable({
  providedIn: 'root',
})
export class LoaderHttpInterceptor implements HttpInterceptor {
  constructor(private _loadingService: LoadingService) {}

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {

    const id = uuid();

    this._loadingService.startLoading(id);

    return next
      .handle(req)
      .pipe(finalize(() => this._loadingService.completeLoading(id)));
  }
}

这些是测试

describe('LoaderHttpInterceptor', () => {
  let mockLoadingService: LoadingService;
  let injector: Injector;
  let httpClient: HttpClient;
  let controller: HttpTestingController;

  beforeEach(() => {
    injector = TestBed.configureTestingModule({
      imports: [TestingModule],
      providers: [
        {
          provide: LoadingService,
          useValue: { startLoading: jest.fn(), completeLoading: jest.fn() },
        },
        {
          provide: HTTP_INTERCEPTORS,
          useClass: LoaderHttpInterceptor,
          multi: true,
          deps: [LoadingService],
        },
      ],
    });
    mockLoadingService = TestBed.get(LoadingService);
    httpClient = TestBed.get(HttpClient);
    controller = TestBed.get(HttpTestingController);
  });

  describe('intercept', () => {
    it('should call loader service startLoading', () => {
      const url = faker.internet.url();
      httpClient.get(url, { responseType: 'text' }).subscribe(() => {
        expect(mockLoadingService.startLoading).toBeCalledTimes(1);
      });

      controller.expectOne(url).flush('');
    });

    it('should call loader service completeLoading on success', async(() => {
      const url = faker.internet.url();

      httpClient.get(url, { responseType: 'text' })
        .subscribe(() => {
        expect(mockLoadingService.completeLoading).toBeCalledTimes(1);
      });

      controller.expectOne(url).flush({}, { status: 200, statusText: ''} );

      controller.verify();
    }));

    it('should call loader service completeLoading on error', async(() => {
      const url = faker.internet.url();

      httpClient.get(url)
        .subscribe(
          () => {},
          () => {
            expect(mockLoadingService.completeLoading).toBeCalledTimes(1);
          });

      controller.expectOne(url).error(new ErrorEvent(faker.random.words(3)), {
        status: 500
      });
      controller.verify();
    }));
  });
});

在最后两个测试中,测试中的expect语句在拦截器中的finalize运算符之前被调用,我不知道为什么。我的印象是,直到拦截器完成,http调用才返回。

如果我将pipe(delay(1000))添加到第二个测试中,它将起作用,但这是错误的。在第三次测试中这样做没有区别。

我希望有人可以向我解释一下。

谢谢。

1 个答案:

答案 0 :(得分:0)

我想这有助于重新(阅读)文档和源代码。我在想测试必须是异步的,因为普通的http请求是异步的,但是HttpTestingController是同步发出的。测试需要这样写:

    it('should call loader service completeLoading on success', () => {
      const url = faker.internet.url();

      httpClient.get(url, { responseType: 'text' }).subscribe();

      controller.expectOne(url).flush({}, { status: 200, statusText: ''} );
      controller.verify();
      expect(mockLoadingService.completeLoading).toBeCalledTimes(1);
    });