使用 Karma 和 Jasmine 对具有多个 API 并行调用的服务进行单元测试 / Angular 8
Unit testing for service with multiple API calls in parallel using Karma and Jasmine / Angular 8
我正在尝试为服务和组件编写单元测试(Jasmine 和 Karma),其目的是对星球大战 API 执行两次 API 调用(更多信息在 https://swapi.co/) 与两个不同的 URLS
,以获取人员和星舰数据。两个调用都是 return 同时使用 forkJoin
.
我的 App
组件调用 ApiService
执行两个调用如下:
应用组件:
import {Component, OnInit} from '@angular/core';
import {ApiService} from '../services/api.service';
export class AppComponent implements OnInit {
constructor(private apiService: ApiService) {}
onGetData() {
this.apiService.getData().subscribe((data: any) => {
this.peopleData = data[0];
this.starshipData = data[1];
});
}
API 服务:
import {HttpClient} from '@angular/common/http';
export class ApiService {
constructor(private http: HttpClient) {}
getData(): Observable<any[]> {
const randomPeoplePage = Math.floor(Math.random() * 9) + 1;
const peopleUrl = `https://swapi.co/api/people/?page=${randomPeoplePage}`;
const randomStarshipsPage = Math.floor(Math.random() * 4) + 1;
const starshipsUrl = `https://swapi.co/api/starships/?page=${randomStarshipsPage}`;
const peopleProm = this.http.get(peopleUrl);
const starshipProm = this.http.get(starshipsUrl);
return forkJoin([peopleProm, starshipProm]);
}
}
我在测试方面相当缺乏经验,尤其是在如何正确测试 API 调用方面。
在过去的几天里,我一直在尝试不同的解决方案,就像这样:
it('should return an Observable', () => {
const randomPeoplePage = Math.floor(Math.random() * 9) + 1;
const randomStarshipsPage = Math.floor(Math.random() * 4) + 1;
const dummyData = [
{
count: 87,
next: `https://swapi.co/api/people/?page=${randomPeoplePage + 1}` || null,
previous: `https://swapi.co/api/people/?page=${randomPeoplePage - 1}` || null,
results: new Array(87)
},
{
count: 37,
next: `https://swapi.co/api/starships/?page=${randomStarshipsPage + 1}` || null,
previous: `https://swapi.co/api/starships/?page=${randomStarshipsPage - 1}` || null,
results: new Array(37)
}
];
service.getData().subscribe(data => {
expect(data.length).toBe(2);
expect(data).toEqual(dummyData);
const peopleReq = httpMock.expectOne(`https://swapi.co/api/people/?page=${randomPeoplePage}`);
const starshipReq = httpMock.expectOne(`https://swapi.co/api/starships/?page=${randomStarshipsPage}`);
forkJoin([peopleReq, starshipReq]).subscribe(() => {
expect(peopleReq.request.method).toBe('GET');
expect(starshipReq .request.method).toBe('GET');
})
peopleReq.flush(dummyData[0]);
starshipReq.flush(dummyData[1]);
});
});
});
但是这个测试没有通过,我真的不确定为什么或者我应该调查什么。
我是否需要测试 api.service.spec.ts
或 app.component.spec.ts
中的 API 调用?
我如何模拟return?我应该单独测试它们吗?
这是一个 'academic' 测试,因此欢迎任何有关最佳实践的提示。
非常感谢!
组件 应包含与数据的表示 有关的逻辑。现在,您可以同时拥有 Smart 和 Dumb 组件。
对于 Smart 组件,您可以使用提供数据(存储、路由、提供数据的服务),而对于 Dumb 组件,您可以测试 data 是否 正确显示 and/or 如果 @Output
事件发送正确。
一个服务应该包含业务逻辑(api调用,可能存储数据等...),逻辑与数据表示不直接相关。
在这种情况下,我只是为 getData()
提供一些预定义数据,这样我就不会调用 real API .
it('....', () => {
const url1 = 'url1';
const url2 = 'url2';
const responseMap = {
url1: { data: 'url1' },
url2: { data: 'url2' }
}
// Assuming this is imported..
class ApiService {
constructor (private http) { }
getData () {
}
}
const getSpy = jasmine.createSpy('Http.get').and.callFake((arg) => {
return of(responseMap[arg]);
});
const mockHttp = { get: getSpy }
const apiService = new ApiService(mockHttp);
spyOn(apiService, 'getData').and.callFake(function () {
return forkJoin([this.http.get(url1), this.http.get(url2)])
});
apiService.getData()
.subscribe(([r1, r2]) => {
expect(getSpy).toHaveBeenCalledWith(url1);
expect(getSpy).toHaveBeenCalledWith(url2);
expect(r1).toBe(responseMap['url1']);
expect(r2).toBe(responseMap['url2']);
})
});
我正在尝试为服务和组件编写单元测试(Jasmine 和 Karma),其目的是对星球大战 API 执行两次 API 调用(更多信息在 https://swapi.co/) 与两个不同的 URLS
,以获取人员和星舰数据。两个调用都是 return 同时使用 forkJoin
.
我的 App
组件调用 ApiService
执行两个调用如下:
应用组件:
import {Component, OnInit} from '@angular/core';
import {ApiService} from '../services/api.service';
export class AppComponent implements OnInit {
constructor(private apiService: ApiService) {}
onGetData() {
this.apiService.getData().subscribe((data: any) => {
this.peopleData = data[0];
this.starshipData = data[1];
});
}
API 服务:
import {HttpClient} from '@angular/common/http';
export class ApiService {
constructor(private http: HttpClient) {}
getData(): Observable<any[]> {
const randomPeoplePage = Math.floor(Math.random() * 9) + 1;
const peopleUrl = `https://swapi.co/api/people/?page=${randomPeoplePage}`;
const randomStarshipsPage = Math.floor(Math.random() * 4) + 1;
const starshipsUrl = `https://swapi.co/api/starships/?page=${randomStarshipsPage}`;
const peopleProm = this.http.get(peopleUrl);
const starshipProm = this.http.get(starshipsUrl);
return forkJoin([peopleProm, starshipProm]);
}
}
我在测试方面相当缺乏经验,尤其是在如何正确测试 API 调用方面。 在过去的几天里,我一直在尝试不同的解决方案,就像这样:
it('should return an Observable', () => {
const randomPeoplePage = Math.floor(Math.random() * 9) + 1;
const randomStarshipsPage = Math.floor(Math.random() * 4) + 1;
const dummyData = [
{
count: 87,
next: `https://swapi.co/api/people/?page=${randomPeoplePage + 1}` || null,
previous: `https://swapi.co/api/people/?page=${randomPeoplePage - 1}` || null,
results: new Array(87)
},
{
count: 37,
next: `https://swapi.co/api/starships/?page=${randomStarshipsPage + 1}` || null,
previous: `https://swapi.co/api/starships/?page=${randomStarshipsPage - 1}` || null,
results: new Array(37)
}
];
service.getData().subscribe(data => {
expect(data.length).toBe(2);
expect(data).toEqual(dummyData);
const peopleReq = httpMock.expectOne(`https://swapi.co/api/people/?page=${randomPeoplePage}`);
const starshipReq = httpMock.expectOne(`https://swapi.co/api/starships/?page=${randomStarshipsPage}`);
forkJoin([peopleReq, starshipReq]).subscribe(() => {
expect(peopleReq.request.method).toBe('GET');
expect(starshipReq .request.method).toBe('GET');
})
peopleReq.flush(dummyData[0]);
starshipReq.flush(dummyData[1]);
});
});
});
但是这个测试没有通过,我真的不确定为什么或者我应该调查什么。
我是否需要测试 api.service.spec.ts
或 app.component.spec.ts
中的 API 调用?
我如何模拟return?我应该单独测试它们吗?
这是一个 'academic' 测试,因此欢迎任何有关最佳实践的提示。
非常感谢!
组件 应包含与数据的表示 有关的逻辑。现在,您可以同时拥有 Smart 和 Dumb 组件。
对于 Smart 组件,您可以使用提供数据(存储、路由、提供数据的服务),而对于 Dumb 组件,您可以测试 data 是否 正确显示 and/or 如果 @Output
事件发送正确。
一个服务应该包含业务逻辑(api调用,可能存储数据等...),逻辑与数据表示不直接相关。
在这种情况下,我只是为 getData()
提供一些预定义数据,这样我就不会调用 real API .
it('....', () => {
const url1 = 'url1';
const url2 = 'url2';
const responseMap = {
url1: { data: 'url1' },
url2: { data: 'url2' }
}
// Assuming this is imported..
class ApiService {
constructor (private http) { }
getData () {
}
}
const getSpy = jasmine.createSpy('Http.get').and.callFake((arg) => {
return of(responseMap[arg]);
});
const mockHttp = { get: getSpy }
const apiService = new ApiService(mockHttp);
spyOn(apiService, 'getData').and.callFake(function () {
return forkJoin([this.http.get(url1), this.http.get(url2)])
});
apiService.getData()
.subscribe(([r1, r2]) => {
expect(getSpy).toHaveBeenCalledWith(url1);
expect(getSpy).toHaveBeenCalledWith(url2);
expect(r1).toBe(responseMap['url1']);
expect(r2).toBe(responseMap['url2']);
})
});