test broke when changed model with interfaces to c

2019-09-13 21:44发布

问题:

Sorry if its a bit long but I wanted to explain myself well.

I have a test for an api call im performing from my service to receive a simple json. (Karma-jasmin/angular2)

this is the call from my service:

@Injectable()
export class MyService {

  constructor(private _myApiService:MyAPIService) {
  }

  public getCarsData():Observable<Car[]> {
    return this._myApiService.getCurrentCarData().map(res => res.carList);
  }

}

this is getCurrentCarData():

export interface CarsListResponse extends ServerResponse {
  carList: Cars[];
}

 public getCurrentCarData(): Observable<CarsListResponse> {
    let apiURL = 'my/api/url'
    return this.http.get(apiURL),
      (ret, retJson) => ret.status === 200 ? {carList: retJson.cars.map(element => new Car(element.year, element.make))} : undefined);
  }

Car interface is:

export interface Car {
  make:string;
  year:number;
}

the json Im getting looks like this (part of a mock):

   status: 200,
        headers: new Headers(HEADERS),
        body: {
          cars: [
            {
              "make": "Mercedes",
              "year": 2016
            },
            {
              "make": "Audi",
              "year": 2017
            },
            {
              "make": "BMW",
              "year": 2015
            }
          ]
        }

Test: the test is for getCurrentCarData():

let carsResponse = () => {
  return { cars: [
            {
              "make": "Mercedes",
              "year": 2016
            },
            {
              "make": "Audi",
              "year": 2017
            },
            {
              "make": "BMW",
              "year": 2015
            }
          ]}
};

let carsExpectedResponse = () => {
  return  [
            {
              "make": "Mercedes",
              "year": 2016
            },
            {
              "make": "Audi",
              "year": 2017
            },
            {
              "make": "BMW",
              "year": 2015
            }
          ]
};
describe('GET Car Data', () => {

    it('should handle respond', inject([XHRBackend, api.MyApiService], (mock, myApiService) => {

      let c:Connection = null;
      mock.connections.subscribe((connection) => {
        connection.mock(new Response(new ResponseOptions({
          status: 200,
          headers: jsoHeaders(),
          body: carsResponse()
        })));
        c = connection;
      });

      myApiService.getCurrentCarData().subscribe(
        res => {
          expect(c.request.url).toEqual(`my/api/url`);
          expect(res.carList).toEqual(carsExpectedResponse());
        },
        error => {
          expect(false).toBeTruthy();
        }
      );
    }));

  });

Ok, so this works! THE problem is when I changed my model from this

interface:

    export interface Car {
      make:string;
      year:number;
    }

to this class:

export class Car implements GenType {
          make:string;
          year:number;

  constructor(make:string, year:number) {
    this.make = make;
    this.year = year;
  }

  displayFormat:() => string = function () {
    return 'someStr'
  }
}

export interface GenType {
  displayFormat: () => string;
}

so now the error im getting is:

 Expected 
           [MatcherEntityBulk({ displayFormat: Function, make: 'Mercedes', year: 2016 }),
            MatcherEntityBulk({ displayFormat: Function, make: 'Audi', year: 2017 }),
            MatcherEntityBulk({ displayFormat: Function, make: 'BMW', year: 2015 })] 
to equal 
           [Object({ displayFormat: Function, make: 'Mercedes', year: 2016 }),
            Object({ displayFormat: Function, make: 'Audi', year: 2017 }), 
            Object({ displayFormat: Function, make: 'BMW', year: 2015 })]

So the problem is pretty obvious, but how do I fix this, I mean with the current change what would be a proper way to change that test?

thanks a bunch for the ones that survived to this point :)

回答1:

toEqual expects carsExpectedResponse() array elements to be instances of certain class (for example, Car). It tests object constructors and expects them to have non-enumerable property constructor that contains a constructor.

It can be a real instance:

let carsExpectedResponse = () => [
  new Car(...),
  ...
];

It can be a fake non-enumerable constructor property:

let carsExpectedResponse = () => [
  {
    "make": "Mercedes",
    "year": 2016
  },
  ...
].map(obj => Object.defineProperty(obj, 'constructor', { value: Car }));

It can be an object constructed with desired prototype chain:

let carsExpectedResponse = () => [
  {
    "make": "Mercedes",
    "year": 2016
  },
  ...
].map(obj => Object.assign(Object.create(Car.prototype), obj));

The last fixture object is probably the most solid, because it doesn't depend on Jasmine internal logic and doesn't depend on constructor logic, too.