Как смоделировать Angular 4.3 httpClient ответ об ошибке при тестировании

У меня ниже interceptor auth-interceptor.service.ts

import {Injectable, Injector} from '@angular/core';
import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Observable} from 'rxjs/Observable';
import {Cookie} from './cookie.service';
import {Router} from '@angular/router';
import {UserService} from './user.service';
import {ToasterService} from '../toaster/toaster.service';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
    constructor(private injector: Injector) {}

    private handleError(err: HttpErrorResponse): Observable<any> {
        let errorMsg;
        if (err.error instanceof Error) {
            // A client-side or network error occurred. Handle it accordingly.
            errorMsg = `An error occurred: ${err.error.message}`;
        } else {
            // The backend returned an unsuccessful response code.
            // The response body may contain clues as to what went wrong,
            errorMsg = `Backend returned code ${err.status}, body was: ${err.error}`;
        }
        if (err.status === 401 || err.status === 403) {
            this.injector.get(UserService).purgeAuth();
            this.injector.get(ToasterService).showError(`Unauthorized`, errorMsg);
            this.injector.get(Router).navigateByUrl(`/login`);
        }
        console.error(errorMsg);
        return Observable.throw(errorMsg);
    }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // Clone the request to add the new header.
        const authReq = req.clone({headers: req.headers.set(Cookie.tokenKey, Cookie.getToken())});
        // Pass on the cloned request instead of the original request.
        return next.handle(authReq).catch(err => this.handleError(err));
    }
}

Теперь я пытаюсь издеваться над http.get, чтобы выдать ошибку, поэтому метод handleError утешает сообщение об ошибке.

Ниже мой подход к тестовому случаю auth-interceptor.service.specs.ts

import {async, inject, TestBed} from '@angular/core/testing';

import {AuthInterceptor} from './auth-interceptor.service';
import {ApiService} from './api.service';
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
import {environment} from '../../../environments/environment';

describe(`AuthInterceptor`, () => {
    const somePath = `/somePath`;

    beforeEach(() => {
        TestBed.configureTestingModule({
            imports: [HttpClientTestingModule],
            providers: [AuthInterceptor, ApiService]
        });
    });

    it(`should be created`, inject([AuthInterceptor], (service: AuthInterceptor) => {
        expect(service).toBeTruthy();
    }));


    it(`should log an error to the console on error on get()`, async(inject([ApiService, HttpTestingController],
        (apiService: ApiService, httpMock: HttpTestingController) => {
            spyOn(console, 'error');
            apiService.get(somePath).subscribe((res) => {
                console.log(`in success:`, res);
            }, (error) => {
                console.log(`in error:`, error);
            });

            const req = httpMock.expectOne(`${environment.apiUri}${somePath}`);
            req.flush({
                type: 'ERROR',
                status: 404,
                body: JSON.stringify({color: `blue`})
            });
            expect(console.error).toHaveBeenCalled();
        }))
    );
});

При сбросе ответа я не уверен, как сбросить ответ об ошибке, чтобы в моем перехватчике вызывался метод handleError, который в конечном итоге вызывает console.error. В документации нет примера моей ситуации. Любая помощь или предложение приветствуется.


person Sai Ram Gupta    schedule 03.09.2017    source источник
comment
Ваш объект req является экземпляром класса TestRequest. Он также имеет метод error(). Вы пробовали что-то вроде req.error(new ErrorEvent('fail'), {status: 404});?   -  person Anton Belonovich    schedule 08.09.2017


Ответы (1)


Метод expectOne в классе HttpTestingController возвращает объект TestRequest. Этот класс TestRequest имеет метод flush, который можно использовать для доставки

как успешные, так и неуспешные ответы.

Мы можем разрешить запрос, вернув тело вместе с некоторыми дополнительными заголовками ответа (если они есть). Соответствующую информацию можно найти здесь.

Теперь вернемся к тому, как вы можете это сделать. Вы можете настроить приведенный ниже фрагмент кода в соответствии с вашим вариантом использования.

http = TestBed.get(HttpTestingController);
let response: any;
let errResponse: any;
const mockErrorResponse = { status: 400, statusText: 'Bad Request' };
const data = 'Invalid request parameters';
apiService.get(somePath).subscribe(res => response = res, err => errResponse = err);
http.expectOne('url/being/monitored').flush(data, mockErrorResponse);
expect(errResponse).toBe(data);

ПРИМЕЧАНИЕ. На момент написания этого комментария в mockErrorResponse требуется statusText. Соответствующую информацию можно найти здесь.

P.S.: метод error класса TestRequest можно использовать для имитации сетевой ошибки в нашем тестовом примере, так как он ожидает экземпляр Error. Следующий фрагмент кода показывает это.

http.expectOne(someUrl).error(new ErrorEvent('network error'));
person Rohit Sharma    schedule 28.09.2017
comment
Если вы проверяете ошибку, не следует ли вместо этого сделать http.expectOne(someUrl).error(null, mockErrorResponse)? Создание ErrorEvent не позволяет вам включать код состояния, поэтому мне это кажется менее желательным, чем мое предложенное решение. - person Michael McKenna; 10.05.2019
comment
Я борюсь с этим. Если у меня есть один модульный тест жасмина, который делает это, он работает. Если у меня их несколько, это на самом деле выдает ошибку, которую я пытаюсь высмеять, и убивает тестовый цикл. - person Jimbo; 05.07.2020
comment
@Джимбо, у меня была такая же проблема. Моя проблема заключалась в том, что я не добавлял обратный вызов ошибки. apiService.get(somePath).subscribe(res => response = res, err => errResponse = err); Второй обратный вызов действительно важен, и это может быть даже пустая функция, такая как (err) => {}. - person Alin Ciocan; 14.09.2020