Как повторить неудачные http-запросы только для идемпотентных методов?

#angular #angular-http-interceptors

Вопрос:

Я пишу приложение Angular 10. Мне нужно создать перехватчик, который будет повторять все неудачные идемпотентные HTTP-запросы. Прочитав несколько онлайн-уроков, я написал перехватчик, который, как я думал, сделает свое дело. Затем я написал тест, чтобы убедиться, что он работает. К сожалению, мой тест не провалится; даже когда я изменяю перехватчик, чтобы сделать тест неудачным, он не проваливается.

Вот перехватчик:

 export class RetryInterceptor implements HttpInterceptor {

  // private IDEMPOTENT_METHODS = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'PUT', 'TRACE']; // this is the real prod code.
  private IDEMPOTENT_METHODS = ['POST']; // this should cause my test to fail, but it doesn't.

  constructor(private router: Router, private logger: NGXLogger) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    console.log('here'); // I've confirmed that this is being printed to the console.
    if (this.IDEMPOTENT_METHODS.includes(request.method)){
      console.log('there'); // I've confirmed that this is being printed to the console.
      return next.handle(request)
        .pipe(
          retry(1));
    }
    else{
      return next.handle(request);
    }
  }
}
 

Вот тест:

 describe('RetryInterceptor', () => {

  let httpTestingController: HttpTestingController;
  let http: HttpClient;
  let router: Router;
  let location: Location;
  let fixture;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        HttpClientTestingModule,
        LoggerTestingModule,
        RouterTestingModule,
        TranslateModule.forRoot({
          loader: {
            provide: TranslateLoader,
            useFactory: httpTranslateLoader,
            deps: [HttpClient]
          }
        })
      ],
      providers: [
        {
          provide: HTTP_INTERCEPTORS,
          useClass: RetryInterceptor,
          multi: true,
        },
      ],
    });

    httpTestingController = TestBed.inject(HttpTestingController);
    http = TestBed.inject(HttpClient);
    router = TestBed.inject(Router);
    location = TestBed.inject(Location);
    fixture = TestBed.createComponent(AppRootComponent);
    router.initialNavigation();
  });

  it('should not retry POSTs', fakeAsync( // I am expecting this to fail. 
    () => {
      http.post('/api/resource', {}).subscribe(response => expect(response).toBeTruthy());
      httpTestingController.expectOne({method: 'POST', url: '/api/resource'}).error(
        new ErrorEvent('network error', { message: 'bad request' }), { status: 400 });

      tick(); // the first tick is intended to make a call to the httpTestingController, which will return an error, which will be caught by my Interceptor.
      tick(); // on the second tick, my Interceptor should retry the failed call. this should, in turn, fail the test, because I have not set up a second expectation.
    })
  );
});
 

Может ли кто-нибудь заметить, что я делаю не так? Мой перехватчик сломался? или мой тест провалился?

Комментарии:

1. Я не вижу логики для управления неудачным запросом в вашем RetryInterceptor !

2. @AmirhosseinMehrvarzi Итак, цель состояла в том, чтобы в случае неудачи повторить попытку один раз. Если он снова выйдет из строя, ошибка будет передана по цепочке фильтров другому фильтру обработки ошибок, который я написал.

Ответ №1:

перехватчик в порядке. вам необходимо проверить свой контроллер тестирования http в конце теста, чтобы перехватывать неожиданные запросы:

 // insert this at end of test or in an afterEach() block
httpTestingController.verify()
 

как правило, любой модульный тест, связанный с http-запросами, должен заканчиваться проверкой.

Документы:

https://angular.io/guide/http#testing-http-requests
https://angular.io/api/common/http/testing/HttpTestingController#verify

Комментарии:

1. Да, я почти уверен, что это сработало. Мой полный код можно найти здесь: github.com/AugustZellmer/Angular-Retry-Interceptor