问题描述
这是我的服务级 job.service.ts。它基本上是调用一个 Express 应用程序并获得一个 Job-object 作为结果。它作为 Observable 返回给 getJob() 方法的调用者。
import { Injectable } from '@angular/core';
import {Job} from "./domain-models/Job";
import {Observable} from 'rxjs';
import {HttpClient} from "@angular/common/http";
@Injectable({
providedIn: 'root'
})
export class JobService {
readonly ROOT_URL = 'http://localhost:3000';
constructor(private http: HttpClient) { }
getJob(jobId: string): Observable<any> {
if(jobId){
return this.http.get<Job>(`${this.ROOT_URL}/jobs/${jobId}`);
}else{
return new Observable<Error>( subscriber => {
subscriber.next(new Error('No jobID provided));
})
}
}
}
这是我的 JobService 测试类的规范。使用 spyOn 方法,我拦截了每次调用真实 API 的尝试并返回一个假响应,这显然是 Job 的一个实例。 job.service.spec.ts:
describe('Testing the getJob() function of JobService',()=>{
beforeEach(()=>{
let jobId: string = 'A123456789'
const job: Job = {
actualCost: 100,amountPaid: 50,wishAnalysis: false
};
spyOn(httpService,"get").and.returnValue(of<Job>(job));
})
it('should return an Observable with the corresponding job object matching the id',()=>{
let jobIdPassed: string = 'A123456789'
service.getJob(jobIdPassed).subscribe(value => {
expect(value instanceof Job).toBeTrue();
});
})
})
如果我执行
expect(value instanceof Job).toBeTrue();
测试失败是因为 value 似乎不是 Job 的实例。如果我更改 JobServices getJob() 函数并返回一个手动创建的 Observable,如下所示:
getJob(jobId: string): Observable<any> {
if(jobId){
return new Observable<Job>(subscriber => {
subscriber.next(new Job());
subscriber.complete();
})
}else{
return new Observable<Error>( subscriber => {
subscriber.next(new Error('No jobID provided));
})
}
}
测试有效! 所以问题是为什么 HttpClient 库的 get() 方法没有像预期的那样在 Observable 中提供适当类型的响应?
感谢您花时间阅读它并提供任何帮助!
解决方法
问题一:测试数据设置不正确
您在测试中创建的 job
不是 Job
类的实例:
class Job {
constructor(public actualCost: number,public amountPaid: number,public wishAnalysis: boolean) {}
}
const job: Job = {
actualCost: 100,amountPaid: 50,wishAnalysis: false
};
console.log(job);
console.log(job instanceof Job); //false
问题 2:对 http.get 的错误假设
这是一个常见的陷阱 - http.get<T>
只是一个 convenience generic
http.get<T>
返回解析后的 json 对象。
T
只是为了方便而使用,期望它正确描述数据的形状。但是不会执行运行时检查 - 缺少属性、附加属性、不会检查任何内容(并且在运行时稍后访问数据时会失败)。
特别是,如果 T 是一个类,你肯定会遇到问题,因为返回的数据在运行时不会是 T 的实例 - 如果名称匹配,它可能会填充字段,但原型链不是设置。这意味着
- 方法调用将在运行时失败
- 实例检查失败
解决方案
不要使用类来模拟使用 http.get
获取的数据的形状。
改用类型(或接口)。如果你需要一个类,映射返回的数据。