我正在尝试为一个服务和一个组件编写单元测试(Jasmine 和 Karma),其目的是使用两个不同的 URLS
,获取人员和星舰数据。两个调用均通过 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 调用?
如何模拟返回?我应该单独测试它们吗?
这是一项“学术”测试,因此欢迎任何有关最佳实践的提示。
非常感谢!
最佳答案
组件应包含与数据表示相关的逻辑。现在,您可以同时拥有智能和哑组件。
对于智能组件,您可以测试与提供数据的外部服务(存储、路由、提供数据的服务)的交互,而对于您可以测试哑组件是否数据正确显示和/或@Output
事件是否正确发送。
服务应包含业务逻辑(API 调用,可能存储数据等...),即与数据呈现不直接相关的逻辑。
在本例中,我只需为 getData()
提供一些预定义数据,这样我就可以确保不会调用真实 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']);
})
});
关于angular - 使用 Karma 和 Jasmine/Angular 8 对具有多个并行 API 调用的服务进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59923374/