此拦截器的目标是在服务器需要验证码时重新发送请求。
但是当刷新jwt令牌时可以使用它。
拦截器工作正常,但我无法解释测试失败的原因。
如果响应代码!= 200,该流将永远不会传递到httpClient.get('/ error')。subscribe()中。
以下是可复制演示的链接: 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();
});
});
答案 0 :(得分:1)
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()
将向您显示所有已提出的请求。
答案 1 :(得分:0)
这是我的最终考试:
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);
}));
});