我的模特课是:
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的一个错误。