关于 Angular 最好的事情之一是通过构造函数传递所有依赖项的模式,并将许多依赖项定义为抽象类而不是具体实现。
因此,如果您想测试 SleepService,您可以将模拟逻辑从 SleepService 内部移至其依赖项,让 SleepService 非常简单:
import { Observable } from 'rxjs';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
export class SleepService {
private endpoint = '';
constructor(private http: HttpClient) { }
postSleepDuration(score: string): Observable<any | HttpErrorResponse> {
return this.http.post<any>(this.endpoint, score);
}
}
并提供具有模拟 HttpHandler 依赖项的 HttpClient 实例。这个HttpHandler 实现只需要一个方法handle(),它可以访问正在生成的HttpRequest,因此它可以根据测试做出适当的反应。
它可以像硬编码的响应/错误一样简单,例如在成功的情况下,类似于:
import { of, Observable } from 'rxjs';
import { HttpClient, HttpEvent, HttpHandler, HttpRequest, HttpResponse } from '@angular/common/http';
import { SleepService } from './sleep-service';
const RESULTS_MOCK: any = {
count: 1,
next: null,
previous: null,
status: 200,
results: [
{
id: '1',
name: 'Asleep and In bed results'
}
]
}
class OkHandler implements HttpHandler {
handle(request: HttpRequest<any>): Observable<HttpEvent<any>> {
return of(new HttpResponse({body: RESULTS_MOCK}));
}
}
const okService = new SleepService(new HttpClient(new OkHandler()));
okService.postSleepDuration('test').subscribe({
next(x) { console.log(JSON.stringify(x, null, 2)); }
});
在失败的情况下,类似:
import { throwError, Observable } from 'rxjs';
import { HttpClient, HttpErrorResponse, HttpEvent, HttpHandler, HttpRequest } from '@angular/common/http';
import { SleepService } from './sleep-service';
class ErrHandler implements HttpHandler {
handle(request: HttpRequest<any>): Observable<HttpEvent<any>> {
return throwError(new HttpErrorResponse({ error: mockedError }));
}
}
const errService = new SleepService(new HttpClient(new ErrHandler()));
errService.postSleepDuration('test').subscribe({
error(x) { console.log(JSON.stringify(x, null, 2)); }
});
或者您可以提供单个 HttpHandler 实现,该实现根据其状态具有不同的行为,例如:
import { of, throwError, Observable } from 'rxjs';
import { HttpClient, HttpErrorResponse, HttpEvent, HttpHandler, HttpRequest, HttpResponse } from '@angular/common/http';
import { SleepService } from './sleep-service';
function mockError(errorCode: number, endpoint: string) {
return `${errorCode}: ${endpoint}`;
}
const RESULTS_MOCK: any = {
count: 1,
next: null,
previous: null,
status: 200,
results: [
{
id: '1',
name: 'Asleep and In bed results'
}
]
}
class Handler implements HttpHandler {
errorCode?: number;
handle(request: HttpRequest<any>): Observable<HttpEvent<any>> {
if (this.errorCode !== undefined) {
return throwError(new HttpErrorResponse({ error: mockError(this.errorCode, request.url) }));
} else {
return of(new HttpResponse({body: RESULTS_MOCK}));
}
}
}
const handler = new Handler();
const service = new SleepService(new HttpClient(handler));
service.postSleepDuration('test').subscribe({
next(x) { console.log(JSON.stringify(x, null, 2)); }
});
handler.errorCode = 42;
service.postSleepDuration('test').subscribe({
error(x) { console.log(JSON.stringify(x, null, 2)); }
});