当MockConnection返回400 http状态时,HTTP承诺不会被拒绝

时间:2017-07-22 07:54:04

标签: angular unit-testing karma-jasmine

我的模特课是:

export class RuntimeSettings {
    A: number,
    B: boolean,
    C: string
}

我有一个服务类,它通过Web API抓取并推送数据对象:

import { Injectable } from '@angular/core';
import { Headers, Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import { AppSettings } from './appSettings';
import { RuntimeSettings } from './runtimesettings';

@Injectable()
export class RuntimeSettingsService {

    constructor(private http: Http) { }

    getRuntimeSettings(): Promise<RuntimeSettings> {
        return this.http.get(AppSettings.API_ENDPOINT)
            .toPromise()
            .then(function (response) { 
                let temp: RuntimeSettings = response.json().Z.R;
                return temp;
            })
            .catch(this.handleError);
    }

    updateRuntimeSettings(settings: RuntimeSettings): Promise<any> {
        // assemble the data
        let data: any = {
            R: settings
        };
        // send the PUT request
        return this.http
            .put(AppSettings.API_ENDPOINT, JSON.stringify(data))
            .toPromise()
            .catch(this.handleError);
    }

    private handleError(error: any): Promise<any> {
        console.error('An error occurred', error);  
        return Promise.reject(error.message || error);
    }
}

此服务类的单元测试文件是:

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

import {
    MockBackend,
    MockConnection
} from '@angular/http/testing';

import {
    HttpModule, Http, XHRBackend, Response, ResponseOptions
} from '@angular/http';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';

import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/toPromise';

import { RuntimeSettings } from './runtimesettings';
import { RuntimeSettingsService } from './runtimesettings.service';
import { AppSettings } from './appSettings';

class mm {
    RuntimeSettings: RuntimeSettings;
}

class ff {
    X: number;
    Y: string;
    Z: mm;
}

function makeResponseData(): ff {
    return {
        X: 72,
        Y: "JYL418",
        Z: {
            R: {
                A: 6080,
                B: true,
                C: "Mark XII"
            }
        }
    }
}

function makeRuntimeSettings(): RuntimeSettings {
    return {
        A: 6080,
        B: true,
        C: "Mark XII"
    }
}

describe('RuntimeSettingsService (mockBackend)', () => {
    beforeEach(async(() => {
        TestBed.configureTestingModule({
            imports: [HttpModule],
            providers: [
                RuntimeSettingsService,
                { provide: XHRBackend, useClass: MockBackend }
            ]
        })
            .compileComponents();
    }));

    describe('when getRuntimeSettings', () => {
        let backend: MockBackend;
        let service: RuntimeSettingsService;
        let response: Response;

        beforeEach(inject([Http, XHRBackend], (http: Http, be: MockBackend) => {
            backend = be;
            service = new RuntimeSettingsService(http);
            let options = new ResponseOptions({ status: 200, body: makeResponseData() });
            response = new Response(options);
        }));

        it('should have expected fake settings of C, B and A', async(inject([], () => {
            backend.connections.subscribe((c: MockConnection) => c.mockRespond(response));
            service.getRuntimeSettings()
                .then(settings => {
                    expect(
                        settings.C == "Mark XII"
                        && settings.B == true
                        && settings.A == 6080
                    ).toBeTruthy('should have expected values');
                });
        })));

        it('should treat 500 as an exception', async(inject([], () => {
            let resp = new Response(new ResponseOptions({ status: 500 }));
            backend.connections.subscribe((c: MockConnection) => c.mockRespond(resp));
            service.getRuntimeSettings()
                .then(s => {    // failure is the expected test result
                    fail('should never reach here');
                })
                .catch(err => {
                    //console.log('Vince: ' + err);
                    expect(err).toContain('Cannot read property '); // TODO: seems to be useless ? err should be an object of the response
                });

        })));
    });


    describe('when updateRuntimeSettings', () => {
        let backend: MockBackend;
        let service: RuntimeSettingsService;
        let response: Response;

        beforeEach(inject([Http, XHRBackend], (http: Http, be: MockBackend) => {
            backend = be;
            service = new RuntimeSettingsService(http);
            let options = new ResponseOptions({ status: 204 });
            response = new Response(options);
        }));

        it('should have expected data in the request', async(inject([], () => {
            backend.connections.subscribe(
                (c: MockConnection) => {
                    let body = c.request.json();
                    expect(
                        body.R.A == 6068
                        && body.B == true
                        && body.C == "Mark XII"
                    ).toBeTruthy('the data pushed through the PUT api should be....');
                    //console.log(body);
                    c.mockRespond(response);
                }
            );
            service.updateRuntimeSettings(makeRuntimeSettings());
        })));

        it('should treat 400 as an exception', async(inject([], () => {
            let resp = new Response(
                new ResponseOptions({ status: 400, body: '{"Message": "The request is invalid."}' })
            );

            backend.connections.subscribe(
                (c: MockConnection) => c.mockRespond(resp)
            );

            service.updateRuntimeSettings(makeRuntimeSettings())
                .then(s => {
                    //console.log('V: ' + s.json().Message);
                    //fail('should never reach here');
                    // TODO: failure is the expected test result, the execution should not get here, but it does reach here
                    expect(400 == s.status
                        && 'The request is invalid.' == s.json().Message)
                        .toBeTruthy('the status code should be 400');
                })
                .catch(err => {
                    console.log('Vince: ' + err);
                    expect(err.json().Message).toContain('The request is invalid.'); // TODO: seems to be useless ?
                    // TODO: the execution should get here, but it does not get here
                });

        })));

    });

});

我的问题是,在getRuntimeSettings()的第二个测试用例中,返回状态500将导致promise被拒绝,因为它的执行转到catch()而不是传递给then()的回调。此行为与真正的ajax调用的情况相同。

但是在updateRuntimeSettings()的第二次测试中,模拟后端向PUT请求返回400 http状态,并且解析了promise对象,因为执行进入传递给then()的回调。这与真正的ajax调用不同。比方说,如果我用真正的服务器端API调用它,并且API返回400响应,它将被拒绝。

它与HTTP方法有什么关系吗?使用MockConnection时是否需要指定PUT?或者这只是MockConnection的一个错误。

0 个答案:

没有答案