Angular observable 中的 http get 响应未在单元测试中进行类型转换
Angular http get response in observable is not typecasting in unit tests
我正在尝试学习 angular 内置单元测试。
我有使用 http.get 管道进入映射的方法的服务,以及 returns 类型的可观察流 (Observable)。
但是,当我尝试使用 angular.io“测试服务”页面中的逻辑对其进行测试时,我不断收到错误 Expected $[0] to be a kind of BankAccountFull, but was Object({...
。
尝试在测试的箭头函数参数和 expect 内部进行类型转换。
服务代码:
import { Injectable } from '@angular/core';
import { catchError, map } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';
import { BankAccountFull } from '../types/bank-account';
import { Observable, of } from 'rxjs';
import { ACCOUNTTYPES, AccountType } from '../types/account-type';
import { TankType, TANKTYPES } from '../types/tank-type';
import { AccList } from '../types/acc-list';
@Injectable({
providedIn: 'root'
})
export class BankAccountService {
private accountsUrl = 'api/accounts';
constructor(private http: HttpClient) { }
getAccountList(token: string): Observable<BankAccountFull[]> {
return this.http.get<BankAccountFull[]>(this.accountsUrl)
.pipe(
map(response => {
const accounts: BankAccountFull[] = response as BankAccountFull[];
return accounts.map((item) => {
const temp: AccountType = ACCOUNTTYPES.find(el => el.id === item.accType);
const tempRes: BankAccountFull = {...item};
if (temp.id) {
tempRes.accTypeHint = temp.hint;
tempRes.accTypeImg = temp.img;
}
const tempTank: TankType = TANKTYPES.find(el => el.id === item.tankType);
if (tempTank.id) {
tempRes.tankTypeImg = tempTank.img;
tempRes.tankTypeName = tempTank.name;
}
return tempRes;
}
);
}),
catchError(this.handleError<BankAccountFull[]>('getHeroes', []))
);
}
private handleError<T>(operation = 'operation', result?: T): (error: any) => Observable<T> {
return (error: any): Observable<T> => {
console.error(error);
return of(result as T);
};
}
}
单元测试代码:
import { BankAccountService } from './bank-account.service';
import { BankAccountFull } from '../types/bank-account';
import { of } from 'rxjs';
import { HttpClient } from '@angular/common/http';
describe('BankAccountService', () => {
let httpClientSpy: {get: jasmine.Spy};
let service: BankAccountService;
beforeEach(() => {
httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']);
service = new BankAccountService(httpClientSpy as any);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
it('should return expected accounts list', () => {
const expectedAccList: BankAccountFull[] =
[
new BankAccountFull(true, 1, '068985 2563', 1, 500000, 0,
'../../assets/img/acc-types/mortgage.png', 'Mortgage', '../../assets/img/tank-types/property-tank.png', 'Property Tank'),
new BankAccountFull(false, 2, '068985 2563', 1, 500000, 0, '../../assets/img/acc-types/mortgage.png', 'Mortgage',
'../../assets/img/tank-types/property-tank.png', 'Property Tank'),
new BankAccountFull(false, 3, '068985 2563', 2, 50000, 1, '../../assets/img/acc-types/car-loan.png', 'Car loan',
'../../assets/img/tank-types/property-tank.png', 'Work Tank'),
new BankAccountFull(false, 4, '068985 2563', 3, 50000, 1, '../../assets/img/acc-types/personal-loan.png', 'Personal credit',
'../../assets/img/tank-types/property-tank.png', 'Work Tank'),
];
httpClientSpy.get.and.returnValue(of(expectedAccList));
service.getAccountList('someMD5Token').subscribe(
(accList: BankAccountFull[]) => expect(accList as BankAccountFull[]).toEqual(expectedAccList, 'expected accounts list'),
fail
);
});
});
测试总结:
BankAccountService > should return expected accounts list
Expected $[0] to be a kind of BankAccountFull, but was Object({ active: true, id: 1, name: '068985 2563', accType: 1, balance: 500000, tankType: 0, accTypeImg: '../../assets/img/acc-types/mortgage.png', accTypeHint: 'Mortgage', tankTypeImg: '../../assets/img/tank-types/property-tank.png', tankTypeName: 'Property Tank' }).
Expected $[1] to be a kind of BankAccountFull, but was Object({ active: false, id: 2, name: '068985 2563', accType: 1, balance: 500000, tankType: 0, accTypeImg: '../../assets/img/acc-types/mortgage.png', accTypeHint: 'Mortgage', tankTypeImg: '../../assets/img/tank-types/property-tank.png', tankTypeName: 'Property Tank' }).
Expected $[2] to be a kind of BankAccountFull, but was Object({ active: false, id: 3, name: '068985 2563', accType: 2, balance: 50000, tankType: 1, accTypeImg: '../../assets/img/acc-types/car-loan.png', accTypeHint: 'Car loan', tankTypeImg: '../../assets/img/tank-types/property-tank.png', tankTypeName: 'Work Tank' }).
Expected $[3] to be a kind of BankAccountFull, but was Object({ active: false, id: 4, name: '068985 2563', accType: 3, balance: 50000, tankType: 1, accTypeImg: '../../assets/img/acc-types/personal-loan.png', accTypeHint: 'Personal credit', tankTypeImg: '../../assets/img/tank-types/property-tank.png', tankTypeName: 'Work Tank' }).
您应该使用 HttpTestingController
对 Http 请求进行单元测试,它有更好的 API (https://medium.com/better-programming/testing-http-requests-in-angular-with-httpclienttestingmodule-3880ceac74cf)。
至于您看到该错误的原因,我怀疑行 const tempRes: BankAccountFull = {...item};
将 tempRes
的类型更改为 Object
。这是一本好书,我认为您正面临类似的情况:https://ultimatecourses.com/blog/ngrx-store-testing-actions (Expected object to be a kind of Object, but was LoadPizzas
.)
我正在尝试学习 angular 内置单元测试。
我有使用 http.get 管道进入映射的方法的服务,以及 returns 类型的可观察流 (Observable
但是,当我尝试使用 angular.io“测试服务”页面中的逻辑对其进行测试时,我不断收到错误 Expected $[0] to be a kind of BankAccountFull, but was Object({...
。
尝试在测试的箭头函数参数和 expect 内部进行类型转换。
服务代码:
import { Injectable } from '@angular/core';
import { catchError, map } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';
import { BankAccountFull } from '../types/bank-account';
import { Observable, of } from 'rxjs';
import { ACCOUNTTYPES, AccountType } from '../types/account-type';
import { TankType, TANKTYPES } from '../types/tank-type';
import { AccList } from '../types/acc-list';
@Injectable({
providedIn: 'root'
})
export class BankAccountService {
private accountsUrl = 'api/accounts';
constructor(private http: HttpClient) { }
getAccountList(token: string): Observable<BankAccountFull[]> {
return this.http.get<BankAccountFull[]>(this.accountsUrl)
.pipe(
map(response => {
const accounts: BankAccountFull[] = response as BankAccountFull[];
return accounts.map((item) => {
const temp: AccountType = ACCOUNTTYPES.find(el => el.id === item.accType);
const tempRes: BankAccountFull = {...item};
if (temp.id) {
tempRes.accTypeHint = temp.hint;
tempRes.accTypeImg = temp.img;
}
const tempTank: TankType = TANKTYPES.find(el => el.id === item.tankType);
if (tempTank.id) {
tempRes.tankTypeImg = tempTank.img;
tempRes.tankTypeName = tempTank.name;
}
return tempRes;
}
);
}),
catchError(this.handleError<BankAccountFull[]>('getHeroes', []))
);
}
private handleError<T>(operation = 'operation', result?: T): (error: any) => Observable<T> {
return (error: any): Observable<T> => {
console.error(error);
return of(result as T);
};
}
}
单元测试代码:
import { BankAccountService } from './bank-account.service';
import { BankAccountFull } from '../types/bank-account';
import { of } from 'rxjs';
import { HttpClient } from '@angular/common/http';
describe('BankAccountService', () => {
let httpClientSpy: {get: jasmine.Spy};
let service: BankAccountService;
beforeEach(() => {
httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']);
service = new BankAccountService(httpClientSpy as any);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
it('should return expected accounts list', () => {
const expectedAccList: BankAccountFull[] =
[
new BankAccountFull(true, 1, '068985 2563', 1, 500000, 0,
'../../assets/img/acc-types/mortgage.png', 'Mortgage', '../../assets/img/tank-types/property-tank.png', 'Property Tank'),
new BankAccountFull(false, 2, '068985 2563', 1, 500000, 0, '../../assets/img/acc-types/mortgage.png', 'Mortgage',
'../../assets/img/tank-types/property-tank.png', 'Property Tank'),
new BankAccountFull(false, 3, '068985 2563', 2, 50000, 1, '../../assets/img/acc-types/car-loan.png', 'Car loan',
'../../assets/img/tank-types/property-tank.png', 'Work Tank'),
new BankAccountFull(false, 4, '068985 2563', 3, 50000, 1, '../../assets/img/acc-types/personal-loan.png', 'Personal credit',
'../../assets/img/tank-types/property-tank.png', 'Work Tank'),
];
httpClientSpy.get.and.returnValue(of(expectedAccList));
service.getAccountList('someMD5Token').subscribe(
(accList: BankAccountFull[]) => expect(accList as BankAccountFull[]).toEqual(expectedAccList, 'expected accounts list'),
fail
);
});
});
测试总结:
BankAccountService > should return expected accounts list
Expected $[0] to be a kind of BankAccountFull, but was Object({ active: true, id: 1, name: '068985 2563', accType: 1, balance: 500000, tankType: 0, accTypeImg: '../../assets/img/acc-types/mortgage.png', accTypeHint: 'Mortgage', tankTypeImg: '../../assets/img/tank-types/property-tank.png', tankTypeName: 'Property Tank' }).
Expected $[1] to be a kind of BankAccountFull, but was Object({ active: false, id: 2, name: '068985 2563', accType: 1, balance: 500000, tankType: 0, accTypeImg: '../../assets/img/acc-types/mortgage.png', accTypeHint: 'Mortgage', tankTypeImg: '../../assets/img/tank-types/property-tank.png', tankTypeName: 'Property Tank' }).
Expected $[2] to be a kind of BankAccountFull, but was Object({ active: false, id: 3, name: '068985 2563', accType: 2, balance: 50000, tankType: 1, accTypeImg: '../../assets/img/acc-types/car-loan.png', accTypeHint: 'Car loan', tankTypeImg: '../../assets/img/tank-types/property-tank.png', tankTypeName: 'Work Tank' }).
Expected $[3] to be a kind of BankAccountFull, but was Object({ active: false, id: 4, name: '068985 2563', accType: 3, balance: 50000, tankType: 1, accTypeImg: '../../assets/img/acc-types/personal-loan.png', accTypeHint: 'Personal credit', tankTypeImg: '../../assets/img/tank-types/property-tank.png', tankTypeName: 'Work Tank' }).
您应该使用 HttpTestingController
对 Http 请求进行单元测试,它有更好的 API (https://medium.com/better-programming/testing-http-requests-in-angular-with-httpclienttestingmodule-3880ceac74cf)。
至于您看到该错误的原因,我怀疑行 const tempRes: BankAccountFull = {...item};
将 tempRes
的类型更改为 Object
。这是一本好书,我认为您正面临类似的情况:https://ultimatecourses.com/blog/ngrx-store-testing-actions (Expected object to be a kind of Object, but was LoadPizzas
.)