Тестовый угловой HttpInterceptor - PullRequest
       30

Тестовый угловой HttpInterceptor

0 голосов
/ 11 сентября 2018

Целью этого перехватчика является повторная отправка запроса, когда сервер запрашивает ключ-капчу.

Но его можно использовать при обновлении токена jwt.

Перехватчик работает нормально, но я не могу объяснить, почему тест не пройден.

Поток никогда не перейдет в httpClient.get ('/ error'). Subscribe (), если код ответа!= 200.

Вот ссылка на воспроизводимый демонстрационный пример: https://stackblitz.com/edit/angular-testing-template-mfqwpj?embed=1&file=app/interceptor.spec.ts

import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Observable} from 'rxjs';
import {Injectable} from '@angular/core';
import {catchError, switchMap} from 'rxjs/operators';
import {CaptchaHeader, CaptchaV2Service} from 'century-lib';


@Injectable({
  providedIn: 'root'
})
export class CaptchaInterceptor implements HttpInterceptor {

  constructor(private captchaService: CaptchaV2Service) {
  }


  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      catchError(err => {
        if (!this.captchaIsRequired(err)) {
          return;
        }
        return this.captchaService.getCaptchaKey().pipe(
          switchMap((key) => {
            const newReq = this.applyCaptchaKey(req, key);
            return next.handle(newReq);
          })
        );
      })
    );
  }

  applyCaptchaKey(req, key) {
    return req.clone({
      headers: req.headers.set('Captcha-Token', key)
    });
  }

  private captchaIsRequired(error) {
    return (error.status === 400 && error.headers.get('Captcha-Status') === 'required');
  }

}

Тест:

import {async, TestBed} from '@angular/core/testing';
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
import {CaptchaV2Service} from 'century-lib';
import {HTTP_INTERCEPTORS, HttpClient, HttpHeaders} from '@angular/common/http';
import {CaptchaInterceptor} from './captcha.interceptor';
import {EventEmitter} from '@angular/core';

class MockCaptchaService {
  valid = new EventEmitter<string>();
  reset = new EventEmitter<boolean>();

  getCaptchaKey() {
    setTimeout(() => {
      this.valid.emit('captcha-key');
    }, 500);
    return this.valid;
  }
}

describe('Captcha interceptor', () => {
  let httpClient: HttpClient;
  let httpMock: HttpTestingController;
  let interceptor: CaptchaInterceptor;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [
        CaptchaInterceptor,
        {provide: CaptchaV2Service, useValue: new MockCaptchaService()},
        {provide: HTTP_INTERCEPTORS, useClass: CaptchaInterceptor, multi: true},
      ]
    });

    httpClient = TestBed.get(HttpClient);
    httpMock = TestBed.get(HttpTestingController);
    interceptor = TestBed.get(CaptchaInterceptor);
  });


  it('should construct', async(() => {
    expect(interceptor).toBeDefined();
  }));

  it('Should interrogate the captchaService when service returns Captcha-Required', async(() => {
    httpClient.get('/error').subscribe(() => {
    }, () => {
    });
    const req = httpMock.expectOne('/error');
    req.error(new ErrorEvent('Captcha Error'), {
      status: 400,
      statusText: 'Captcha-Error',
      headers: new HttpHeaders().set('Captcha-Status', 'required')
    });
    expect(req.request.headers.get('Captcha-Token')).toBe('captcha-key');
    httpMock.verify();
  }));

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


});

Ответы [ 2 ]

0 голосов
/ 11 сентября 2018

Вот мой последний тест:

import {async, fakeAsync, TestBed, tick} from '@angular/core/testing';
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
import {CaptchaV2Service} from 'century-lib';
import {HTTP_INTERCEPTORS, HttpClient, HttpHeaders} from '@angular/common/http';
import {CaptchaInterceptor} from './captcha.interceptor';
import {Observable} from 'rxjs';


function ObservableDelay<T>(val: T, delay: number, cb = () => {
}): Observable<any> {
  return new Observable(observer => {
    setTimeout(() => {
      observer.next(val);
      observer.complete();
      cb();
    }, delay);
  });
}

const CAPTCHA_TOKEN = 'captcha-token';

describe('Captcha interceptor', () => {
  let httpClient: HttpClient;
  let httpMock: HttpTestingController;
  let interceptor: CaptchaInterceptor;
  let captchaService: CaptchaV2Service;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [
        CaptchaInterceptor,
        {provide: CaptchaV2Service, useClass: CaptchaV2Service},
        {provide: HTTP_INTERCEPTORS, useClass: CaptchaInterceptor, multi: true},
      ]
    });

    httpClient = TestBed.get(HttpClient);
    httpMock = TestBed.get(HttpTestingController);
    interceptor = TestBed.get(CaptchaInterceptor);
    captchaService = TestBed.get(CaptchaV2Service);
  });


  it('should construct', async(() => {
    expect(interceptor).toBeDefined();
  }));

  it('Should interrogate the captchaService when service returns Captcha-Required', fakeAsync(() => {

    spyOn(captchaService, 'getCaptchaKey').and.returnValue(ObservableDelay(CAPTCHA_TOKEN, 200, () => {
      httpMock
        .expectOne(r => r.headers.has('Captcha-Token') && r.headers.get('Captcha-Token') === CAPTCHA_TOKEN);
    }));

    httpClient.get('/error').subscribe();
    const req = httpMock.expectOne('/error');
    req.error(new ErrorEvent('Captcha Error'), {
      status: 400,
      statusText: 'Captcha-Error',
      headers: new HttpHeaders().set('Captcha-Status', 'required')
    });

    tick(200);
  }));


});
0 голосов
/ 11 сентября 2018
const req = httpMock.expectOne('/error');
req.error(new ErrorEvent('Captcha Error'), {
  status: 400,
  statusText: 'Captcha-Error',
  headers: new HttpHeaders().set('Captcha-Status', 'required')
});
expect(req.request.headers.get('Captcha-Token')).toBe('captcha-key');

Это не имеет никакого смысла.У вас есть один запрос req, и вы сбрасываете его с ошибкой.Это нормально, но на этом этапе запрос завершен, и ничего не будет (у вас был запрос, и вы получили ответ).

Теперь в последней строке ожидается обратное - этот завершенный запрос каким-то образом изменится.

Это не то, что делает ваш перехватчик.Перехватчик делает другой запрос на получение нового токена (или проверяет капчу), а затем повторяет исходный запрос.Удалите expect и mock.verify() покажет вам все запросы, которые были сделаны.

...