簡體   English   中英

Angular 10:使用修改響應的 HttpInterceptor 進行單元測試,但未獲得 HttpResponse

[英]Angular 10: Unit Testing with a HttpInterceptor that modifies the response not getting an HttpResponse

如果在 SO 周圍搜索答案,到目前為止,我嘗試過的所有內容都會產生相同的缺失信息。

這是使用最新版本的 Karma/Jasmine 運行 Angular 10。

本質上,我有一個 HTTP 攔截器,它正在查看返回 object 的內容類型。 如果是json,照常繼續,如果是html...則報錯。

import {
    HttpEvent,
    HttpHandler,
    HttpInterceptor,
    HttpRequest,
    HttpResponse
} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { httpStatusCodes } from '../../../shared/enums/httpStatusCodes.enum';
import { errorResponse } from './../../../shared/models/errorResponse.model';

@Injectable()
export class WafErrorInterceptor implements HttpInterceptor {
    intercept(
        request: HttpRequest<any>,
        next: HttpHandler
    ): Observable<HttpEvent<any>> {
        return next.handle(request).pipe(
            map((event: HttpEvent<any>) => {
                console.log(event instanceof HttpResponse);
                if (
                    event instanceof HttpResponse &&
                    event.headers.has('content-type') &&
                    event.headers.get('content-type') === 'application/json'
                ) {
                    return event;
                }

                const throwErrorResponse = new errorResponse(
                    httpStatusCodes.WAF_ERROR,
                    '99999',
                    event instanceof HttpResponse
                        ? event.body
                        : 'unknown error occurred'
                );
                throw throwErrorResponse;
            })
        );
    }
}

然后在我的單元測試中我運行這個:

import {
    HttpClient,
    HttpHeaders,
    HttpResponse,
    HTTP_INTERCEPTORS
} from '@angular/common/http';
import {
    HttpClientTestingModule,
    HttpTestingController
} from '@angular/common/http/testing';
import { fakeAsync, TestBed, tick } from '@angular/core/testing';
import { errorResponse } from './../../../shared/models/errorResponse.model';
import { WafErrorInterceptor } from './waf-error.service';

describe('WafErrorInterceptor', () => {
    let httpMock: HttpTestingController;
    let httpClient: HttpClient;

    beforeEach(() => {
        TestBed.configureTestingModule({
            imports: [HttpClientTestingModule],
            providers: [
                {
                    provide: HTTP_INTERCEPTORS,
                    useClass: WafErrorInterceptor,
                    multi: true
                }
            ]
        });
        httpMock = TestBed.get(HttpTestingController);
        httpClient = TestBed.get(HttpClient);
    });

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

    it('intercept: when no error, then subscribe returns successfully', () => {
        const testData: string = 'test';

        httpClient.get<string>('https://secure.testurl.com/success').subscribe(
            (data) => expect(data).toBeTruthy(),
            (error: errorResponse) => {
                console.log(error);
                fail('error should not have been called');
            }
        );
        tick();
        let req = httpMock.expectOne('https://secure.testurl.com/success');
        tick();

        let httpHeaders = new HttpHeaders();
        httpHeaders.set('content-type', 'application/json');

        const expectedResponse = new HttpResponse<string>({
            status: 200,
            statusText: 'OK',
            body: testData,
            headers: httpHeaders
        });

        //req.flush(expectedResponse);
        req.event(expectedResponse);
    });
});

我已經嘗試在我只發回數據的地方刷新,在哪里發回數據和標題/狀態。 我在哪里發回一個 httpresponse 等。每次,當它進入攔截器時,攔截器看不到 httpresponse 類型的響應,並且 console.log 總是返回 false。

我什至剛剛創建了一個單元測試,其中單元測試發送了一個模擬 object ......即使這樣也有同樣的問題。

想法?

更新:因此以下答案適用於 Map,但在測試 catchErrorInterceptor 時仍然存在問題。 該代碼適用於我的網站。 我們的 API 返回一個 object,其中的錯誤包含一系列錯誤。 所以我們抓住第一個並使用它。

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
    intercept(
        request: HttpRequest<any>,
        next: HttpHandler
    ): Observable<HttpEvent<any>> {
        return next.handle(request).pipe(
            catchError((error: HttpErrorResponse) => {
                let statusCode = httpStatusCodes.CONFLICT;
                let errorMessage = '';
                let errorCode = '99999';

                statusCode =
                    error.status === 0
                        ? httpStatusCodes.INTERNAL_SERVER_ERROR
                        : error.status;

                if (error.error.errors && error.error.errors.length > 0) {
                    errorCode = error.error.errors[0].code;
                    errorMessage = error.error.errors[0].description;
                } else {
                    errorMessage = error.message;
                }

                const throwErrorResponse = new errorResponse(
                    statusCode,
                    errorCode,
                    errorMessage
                );

                return throwError(throwErrorResponse);
            })
        );
    }
}

這是其中一項測試:

    it('intercept: when delibarate 409, then error returned', (done) => {
        httpClient
            .get<string>('https://secure.go2bank.com/error')
            .pipe(skip(1))
            .subscribe(
                (data) => fail('should have failed with the 404 error'),
                (error: errorResponse) => {
                    expect(error).toBeTruthy(); // check if executed
                    expect(error.httpStatusCodes).toBe(
                        httpStatusCodes.CONFLICT
                    );
                    expect(error.errorCode).toBe('1007');
                    expect(error.errorMessage).toBe(
                        'Validation error Authorization'
                    );
                    done();
                }
            );

        const errorInitEvent: ErrorEventInit = {
            message: null,
            error: {
                errors: [
                    {
                        code: '1007',
                        description: 'Validation error Authorization.',
                        message: null,
                        link: null,
                        additionalinfo: null
                    }
                ]
            }),
            lineno: null,
            colno: null,
            filename: null
        };

        let error = new ErrorEvent('ERROR', errorInitEvent);

        httpMock.expectOne('https://secure.go2bank.com/error').error(error, {
            status: httpStatusCodes.CONFLICT,
            statusText: 'Conflict',
            headers: new HttpHeaders().set('content-type', 'application/json')
        });
    });

這個測試的結果總是 99999 而不是 1007。所以我得到了錯誤,它正在捕獲錯誤。 但是當我查看它時,error.error 是 ErrorEvent(isTrusted: //"),而且看起來我在 Error 中沒有錯誤數組。

這里的主要問題是,如果HttpClientTestingModule Angular 使用HttpClientTestingBackend class 而不是 HttpXhrBackend 來模擬實際的 Z80791B3AE7002CB88C246876D9FAA8F8

但是它們的實現有一個很大的不同:

HttpClientTestingBackend總是發送{ type: HttpEventType.Sent }事件,而 HttpXhrBackend 通常發送HttpResponse事件。

這意味着事件{ type: HttpEventType.Sent }是在map rxjs 操作符內部處理的第一個事件,它將失敗。

因此,您需要過濾響應,例如:

攔截器.ts

import {
  HttpEventType,
  ...
} from '@angular/common/http';
...

map((event: HttpEvent<any>) => {
  if (event.type === HttpEventType.Sent) { <---------- add this
    return event;
  }
  
  if (
    event instanceof HttpResponse &&
    event.headers.has('content-type') &&
    event.headers.get('content-type') === 'application/json'
  ) {
    return event;
  }
  ...

更新:或者在您的測試中使用 skip(1) rxjs 運算符會更好。

現在,讓我們回到你的測試。

首先,您需要刪除所有多余的tick()調用。

然后flush方法的簽名略有不同。

flush(body, opts?: {
    headers?: HttpHeaders | {
        [name: string]: string | string[];
    };
    status?: number;
    statusText?: string;
})

但是你試圖把所有東西都放入body中。

所以,這是你的測試用例:

攔截器.spec.ts

it('intercept: when no error, then subscribe returns successfully', () => {
  const testData: string = 'test';

  httpClient.get<string>('https://secure.testurl.com/success').subscribe(
    (data) => expect(data).toBeTruthy(),
    (error: errorResponse) => {
      console.log(error);
      fail('error should not have been called');
    }
  );

  const req = httpMock.expectOne('https://secure.testurl.com/success');

  req.flush(testData, {
    status: 200,
    statusText: 'OK',
    headers: new HttpHeaders().set('content-type', 'application/json')
  });
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM