如何对这个从管道可观测对象捕获错误的Angular typescript Http错误拦截器进行单元测试?

如何对这个从管道可观测对象捕获错误的Angular typescript Http错误拦截器进行单元测试?,angular,typescript,unit-testing,rxjs,Angular,Typescript,Unit Testing,Rxjs,我正在运行一个实验,通过测试其他人的代码(例如,自动单元和端到端测试),学习angular和typescript。在我对它进行测试之后,我计划将它重新用于我正在为一所大学教室进行的一个宠物项目 我在这里对代码进行了至少一半的单元测试: 我已经尝试了一段时间让下面的代码进行单元测试,但我从自己的想法或互联网上的想法中尝试的所有东西到目前为止都没有成功: import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from "@an

我正在运行一个实验,通过测试其他人的代码(例如,自动单元和端到端测试),学习angular和typescript。在我对它进行测试之后,我计划将它重新用于我正在为一所大学教室进行的一个宠物项目

我在这里对代码进行了至少一半的单元测试:

我已经尝试了一段时间让下面的代码进行单元测试,但我从自己的想法或互联网上的想法中尝试的所有东西到目前为止都没有成功:

import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from "@angular/common/http";
import { AuthenticationService } from "src/app/authenticationService/AuthenticationService";
import { Observable, throwError } from "rxjs";
import { catchError } from "rxjs/operators";
import { Injectable } from "@angular/core";

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
    constructor(private authenticationService: AuthenticationService) {}

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        console.log('before error handle')
        return next.handle(request).pipe(catchError(err => {

            console.log('in error handle')

            if (err.status === 401) { 
                // auto logout if 401 response returned from api
                this.authenticationService.logout();
                location.reload(true);
            }

            const error = err.error.message || err.statusText;
            return throwError(error);
        }))
    }

}
import{HttpInterceptor,HttpRequest,HttpHandler,HttpEvent}来自“@angular/common/http”;
从“src/app/AuthenticationService/AuthenticationService”导入{AuthenticationService};
从“rxjs”中导入{可观察,投掷者};
从“rxjs/operators”导入{catchError};
从“@angular/core”导入{Injectable}”;
@可注射()
导出类ErrorInterceptor实现HttpInterceptor{
构造函数(私有authenticationService:authenticationService){}
拦截(请求:HttpRequest,下一步:HttpHandler):可观察{
console.log('错误句柄之前')
返回next.handle(request).pipe(catchError(err=>{
console.log('in error handle')
如果(err.status==401){
//如果api返回401响应,则自动注销
this.authenticationService.logout();
位置。重新加载(true);
}
const error=err.error.message | | err.statusText;
返回投掷器(错误);
}))
}
}
以下测试代码和多个变体未能成功获取要显示在控制台日志中的“错误句柄”消息:

import { ErrorInterceptor } from "./ErrorInterceptor";
import { of, throwError, defer } from "rxjs";

describe('ErrorInterceptor', () => {
    let errorInterceptor;
    let authenticationServiceSpy;

    beforeEach(() => {
        authenticationServiceSpy = jasmine.createSpyObj('AuthenticationService', ['logout']);
        errorInterceptor = new ErrorInterceptor(authenticationServiceSpy);
    })

    it('should create', () => {
        expect(errorInterceptor).toBeTruthy();
    })

    describe('intercept', () => {
        let httpRequestSpy;
        let httpHandlerSpy;
        const error = {status: 401, statusText: 'error'};

        it('should auto logout if 401 response returned from api', () => {
            //arrange
            httpRequestSpy = jasmine.createSpyObj('HttpRequest', ['doesNotMatter']);
            httpHandlerSpy = jasmine.createSpyObj('HttpHandler', ['handle']);
            httpHandlerSpy.handle.and.returnValue({
                pipe: () => {
                return fakeAsyncResponseWithError({});
                }
            });

            //act
            errorInterceptor.intercept(httpRequestSpy, httpHandlerSpy);

            //assert
            //TBD

            function fakeAsyncResponseWithError<T>(data: T) {
                return defer(() => throwError(error));
            }
        })
    })
})
从“/ErrorInterceptor”导入{ErrorInterceptor};
从“rxjs”导入{of,throwError,defer};
描述('ErrorInterceptor',()=>{
让错误拦截器;
让authenticationServiceSpy;
在每个之前(()=>{
authenticationServiceSpy=jasmine.createSpyObj('AuthenticationService',['logout']);
errorInterceptor=新的errorInterceptor(authenticationServiceSpy);
})
它('应该创建',()=>{
expect(errorInterceptor.toBeTruthy();
})
描述('intercept',()=>{
让httpRequestSpy;
让httpHandlerSpy;
常量错误={status:401,statusText:'error'};
它('如果401响应从api返回,则应自动注销',()=>{
//安排
httpRequestSpy=jasmine.createSpyObj('HttpRequest',['doesNotMatter']);
httpHandlerSpy=jasmine.createSpyObj('HttpHandler',['handle']);
httpHandlerSpy.handle.and.returnValue({
管道:()=>{
返回fakeAsyncResponseWithError({});
}
});
//表演
errorInterceptor.intercept(httpRequestSpy、httpHandlerSpy);
//断言
//待定
函数fakeAsyncResponseWithError(数据:T){
返回延迟(()=>throwError(error));
}
})
})
})

这里有几个问题

  • 首先,来自
    httpHandlerSpy.handle()
    的返回值需要是一个可观察的值,因为它上面已经有管道操作符,然后HttpInterceptor代码可以根据需要将其管道化到catchError
  • 第二,HttpInterceptor返回一个可观察的,为了“执行”,需要订阅它
我用一个例子来说明我是如何做到这一点的

以下是Stackblitz中的规范(
it
function):


我希望这能有所帮助。

非常感谢。谢谢它不仅完整而简洁地回答了我的问题,还通过一些可运行的演示代码教给了我一些东西。这个答案对我也很有帮助。谢谢:-)@dmcgrandle,你能回答这个问题吗:我有两个私有方法。
it('should auto logout if 401 response returned from api', () => {
    //arrange
    httpRequestSpy = jasmine.createSpyObj('HttpRequest', ['doesNotMatter']);
    httpHandlerSpy = jasmine.createSpyObj('HttpHandler', ['handle']);
    httpHandlerSpy.handle.and.returnValue(throwError(
        {error: 
            {message: 'test-error'}
        }
    ));
    //act
    errorInterceptor.intercept(httpRequestSpy, httpHandlerSpy)
        .subscribe(
            result => console.log('good', result), 
            err => { 
                console.log('error', err);
                expect(err).toEqual('test-error');
            }
        );

    //assert

})