Angular 2单元测试可观察到的错误(HTTP) [英] Angular 2 Unit Testing Observable Errors (HTTP)

查看:75
本文介绍了Angular 2单元测试可观察到的错误(HTTP)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为我的API服务编写单元测试,但是在捕获HTTP错误时遇到了一些麻烦。我一直在遵循指南和Angular2文档,

I am trying to write unit tests for my API service but have some trouble catching HTTP errors. I am following this guide along with the Angular2 docs since the guide is (slightly) out of date in some minor areas.

所有单元测试都通过了服务抛出错误的测试(由于错误的HTTP状态代码) )。我可以通过注销 response.ok 来说明这一点。根据我的阅读,这与单元测试不是异步执行有关,因此不必等待错误响应。但是,我不知道为什么会出现这种情况,因为我在 beforeEach async()实用程序功能

All unit tests pass apart from those where an error is thrown by the service (due to error HTTP status code). I can tell this by logging out response.ok. From what i've read this has something to do with the unit tests not executing asynchronously, hence, not waiting for the error response. However, I have no idea why this is the case here since I have used the async() utility function in the beforeEach method.

get(endpoint: string, authenticated: boolean = false): Observable<any> {
    endpoint = this.formatEndpoint(endpoint);
    return this.getHttp(authenticated) // Returns @angular/http or a wrapper for handling auth headers
        .get(endpoint)
        .map(res => this.extractData(res))
        .catch(err => this.handleError(err)); // Not in guide but should work as per docs
}
private extractData(res: Response): any {
    let body: any = res.json();
    return body || { };
}

private handleError(error: Response | any): Observable<any> {
    // TODO: Use a remote logging infrastructure
    // TODO: User error notifications
    let errMsg: string;
    if (error instanceof Response) {
        const body: any = error.json() || '';
        const err: string = body.error || JSON.stringify(body);
        errMsg = `${error.status} - ${error.statusText || ''}${err}`;
    } else {
        errMsg = error.message ? error.message : error.toString();
    }
    console.error(errMsg);
    return Observable.throw(errMsg);
}



错误单元测试



Error unit test

// Imports

describe('Service: APIService', () => {
    let backend: MockBackend;
    let service: APIService;

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            providers: [
                BaseRequestOptions,
                MockBackend,
                APIService,
                {
                    deps: [
                        MockBackend,
                        BaseRequestOptions
                    ],
                    provide: Http,
                        useFactory: (backend: XHRBackend, defaultOptions: BaseRequestOptions) => {
                            return new Http(backend, defaultOptions);
                        }
                },
                {provide: AuthHttp,
                    useFactory: (http: Http, options: BaseRequestOptions) => {
                        return new AuthHttp(new AuthConfig({}), http, options);
                    },
                    deps: [Http, BaseRequestOptions]
                }
            ]
        });
        const testbed: any = getTestBed();
        backend = testbed.get(MockBackend);
        service = testbed.get(APIService);
    }));

    /**
     * Utility function to setup the mock connection with the required options
     * @param backend
     * @param options
     */
    function setupConnections(backend: MockBackend, options: any): any {
        backend.connections.subscribe((connection: MockConnection) => {
            const responseOptions: any = new ResponseOptions(options);
            const response: any = new Response(responseOptions);
            console.log(response.ok); // Will return false during the error unit test and true in others (if spyOn log is commented).
            connection.mockRespond(response);
        });
    }

    it('should log an error to the console on error', () => {
        setupConnections(backend, {
            body: { error: `Some strange error` },
            status: 400
        });
        spyOn(console, 'error');
        spyOn(console, 'log');

        service.get('/bad').subscribe(null, e => {
            // None of this code block is executed.
            expect(console.error).toHaveBeenCalledWith("400 - Some strange error");
            console.log("Make sure an error has been thrown");
        });

        expect(console.log).toHaveBeenCalledWith("Make sure an error has been thrown."); // Fails
    });



更新1



当我检查第一个回调response.ok未定义。这使我相信 setupConnections 实用程序中有问题。

Update 1

when I check the first callback, response.ok is undefined. This leads me to believe that there is something wrong in the setupConnections utility.

    it('should log an error to the console on error', async(() => {
        setupConnections(backend, {
            body: { error: `Some strange error` },
            status: 400
        });
        spyOn(console, 'error');
        //spyOn(console, 'log');

        service.get('/bad').subscribe(res => {
            console.log(res); // Object{error: 'Some strange error'}
            console.log(res.ok); // undefined
        }, e => {
            expect(console.error).toHaveBeenCalledWith("400 - Some strange error");
            console.log("Make sure an error has been thrown");
        });

        expect(console.log).toHaveBeenCalledWith("Make sure an error has been thrown.");
    }));



更新2



如果不是在get方法中捕获错误我在map中显式地执行了操作,然后仍然遇到相同的问题。

Update 2

If, instead of catching errors in the get method I do it explicitly in map then still have same problem.

get(endpoint: string, authenticated: boolean = false): Observable<any> {
    endpoint = this.formatEndpoint(endpoint);
    return this.getHttp(authenticated).get(endpoint)
        .map(res => {
            if (res.ok) return this.extractData(res);
            return this.handleError(res);
        })
        .catch(this.handleError);
}



更新3



经过一番讨论后此问题已提交

推荐答案


从我读到的内容来看,这与单元测试不是异步执行有关,因此不必等待错误响应。但是,我不知道为什么会出现这种情况,因为我在 beforeEach async()实用程序功能>方法

From what i've read this has something to do with the unit tests not executing asynchronously, hence, not waiting for the error response. However, I have no idea why this is the case here since I have used the async() utility function in the beforeEach method

您需要在测试用例中使用它( it )。 async 的作用是创建一个测试区域,该区域在完成测试(或测试区域,例如 beforeEach )。

You need to use it in the test case (the it). What async does is create an test zone that waits for all async tasks to complete before completing the test (or test area, e.g. beforeEach).

因此, beforeEach async c>在退出之前仅等待异步任务在方法中完成。但是 it 也需要同样的东西。

So the async in the beforeEach is only waiting for the async tasks to complete in the method before exiting it. But the it also needs that same thing.

it('should log an error to the console on error', async(() => {

}))



UPDATE



除了缺少的 async 外,似乎还有一个错误 MockConnection 。如果您查看 mockRespond ,它总是调用 next ,而不考虑状态码

UPDATE

Aside from the missing async, there seems to be a bug with the MockConnection. If you look at the mockRespond, it always calls next, not taking into consideration the status code

mockRespond(res: Response) {
  if (this.readyState === ReadyState.Done || this.readyState === ReadyState.Cancelled) {
    throw new Error('Connection has already been resolved');
  }
  this.readyState = ReadyState.Done;
  this.response.next(res);
  this.response.complete();
}

他们有一个 mockError(Error) 方法,即调用 error

mockError(err?: Error) {
  // Matches ResourceLoader semantics
  this.readyState = ReadyState.Done;
  this.response.error(err);
}

但这并不意味着您可以通过响应。这与真正的 XHRConnection 起作用,它检查状态,并通过<$发送 Response c $ c> next error ,但与 Response

but this does not call allow you to pass a Response. This is inconsistent with how the real XHRConnection works, which checks for the status, and sends the Response either through the next or error, but is the same Response

response.ok = isSuccess(status);
if (response.ok) {
  responseObserver.next(response);
  // TODO(gdi2290): defer complete if array buffer until done
  responseObserver.complete();
  return;
}
responseObserver.error(response);

听起来像个虫子。您可能应该报告的内容。他们应该允许您在 mockError 中发送 Response 或在 mockRespond ,它们在 XHRConnection 中执行。

Sounds like a bug to me. Something you should probably report. They should allow you to either send the Response in the mockError or do the same check in the mockRespond that they do in the XHRConnection.

当前解决方案

function setupConnections(backend: MockBackend, options: any): any {
    backend.connections.subscribe((connection: MockConnection) => {
        const responseOptions: any = new ResponseOptions(options);
        const response: any = new Response(responseOptions);

        // Have to check the response status here and return the appropriate mock
        // See issue: https://github.com/angular/angular/issues/13690
        if (responseOptions.status >= 200 && responseOptions.status <= 299)
            connection.mockRespond(response);
        else
            connection.mockError(response);
    });
}

这篇关于Angular 2单元测试可观察到的错误(HTTP)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆