typescript - @angular/common/http get-Metodu, sağlanan <type> ile Observable döndürmüyor - o tipe instanceof kullanımı başarısız oluyor


Bu benim Service sınıfı job.service.ts dosyam. Temelde bir Express uygulamasını çağırıyor ve sonuç olarak bir Job nesnesi alıyor. Bunu getJob() metodunu çağırana Observable olarak döndürüyor.

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));
        })

      }
    }
  }

Bu, JobService test sınıfımdan bir spesifikasyondur. spyOn yöntemi ile gerçek API'yi çağırma girişimlerinin her birini engelliyor ve görünüşe göre bir Job örneği olan sahte bir yanıt döndürüyorum. 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();
      });
    })
})

Eğer yürütürsem

expect(value instanceof Job).toBeTrue();

test başarısız oluyor çünkü görünüşe göre değer Job örneği değil. Eğer JobServices getJob() fonksiyonunu değiştirirsem ve şöyle manuel olarak oluşturulmuş bir Observable döndürürsem:

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));
        })

      }
    }

Test çalışıyor! Öyleyse sorun, HttpClient kütüphanesinin get() metodunun, olması gerektiği gibi bir Observable içinde yanıtı uygun türde sağlamaması neden?

Bunu okumak için zaman ayırdığın ve herhangi bir yardımın için minnettarım!

Cevap #1

Problem 1: Test verilerinin yanlış kurulması

Testinizde oluşturduğunuz job, Job sınıfının bir örneği değildir:

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

Problem 2: http.get hakkında yanlış varsayımlar

Bu yaygın bir tuzaktır - http.get<T> sadece bir kolaylık sağlayan generiktir

http.get<T> ayrıştırılmış json nesnesini döndürür.

T yalnızca kolaylık sağlamak için kullanılır, beklenti verinizin şeklini doğru bir şekilde tanımlamasıdır. Ancak hiçbir çalışma zamanı kontrolü yapılmaz - eksik özellikler, ek özellikler, hiçbir şey kontrol edilmeyecek (ve veriye eriştiğinizde daha sonra çalışma zamanında başarısız olabilir).

Özellikle, eğer T bir sınıf ise, sorunlarla karşılaşmanız kaçınılmazdır, çünkü döndürülen veri çalışma zamanında T'nin örneği olmayacaktır - alanlar, isimler eşleşirse doldurulmuş olabilir, ancak prototip zinciri kurulmamıştır. Bu, demektir ki

  • metot çağrıları çalışma zamanında başarısız olacak
  • instanceof kontrolü başarısız oldu

Çözüm

http.get ile elde edilen verilerin şeklini modellemek için bir sınıf kullanmayın. Bunun yerine bir tür (veya arayüz) kullanın. Eğer bir sınıfa ihtiyacınız varsa, döndürülen verileri eşleştirin.