简体   繁体   中英

@angular/common/http get-Method does not return the Observable with the provided <type> - instanceof that type fails

This is my Service-class job.service.ts. It is basically calling a Express app and getting a Job-object as result. Which it is returning as Observable to the caller of the getJob() method.

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

      }
    }
  }

This is a spec from my JobService test class. with the spyOn method I intercept every attempt to call the real API and return a fake response which apparently is an instance of 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();
      });
    })
})

If I execute

expect(value instanceof Job).toBeTrue();

the test fails because it seems that value is not an instance of Job. If I change the JobServices getJob() function and return a manually created Observable like so:

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

      }
    }

The test works? So the problem is why is HttpClient librarys get() method not providing the response in the appropriate type inside an Observable like it's supposed to?

I appreciate that you took the time to read it and for any help!

Problem 1: Incorrect setup of test data

The job you create in your test is not the instance of Job class:

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: Incorrect assumptions about http.get

This is a common pitfall - http.get<T> is only a convenience generic

http.get<T> returns parsed json object.

T is used only for convenience, the expectation is that it describes the shape of your data correctly. But no runtime check is performed - missing properties, additional properties, nothing will be checked (and fail later in runtime, when you access the data).

In particular, if T is a class, you are bound to have issues, as the returned data will not be the instance of T in runtime - it may have the fields filled in, if names match, but prototype chain is not set up. This means that

  • method calls will fail in runtime
  • instanceof check fails

Solution

Don't use a class to model the shape of data aquired with http.get . Use a type (or interface) instead. If you need a class, map the returned data.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM