Angular 如何测试服务方法?
Angular how to test service methods?
我正在努力学习如何编写测试。我不明白如何从服务中模拟和测试方法。
我当前的服务文件如下:
import {Injectable} from '@angular/core';
import {HttpClient, HttpErrorResponse} from '@angular/common/http';
import {Observable, throwError} from 'rxjs';
import {catchError, map, tap} from 'rxjs/operators';
import {APICONFIG} from 'src/app/config/api.config';
import {Certificate} from 'src/app/models';
const APIURL = `${APICONFIG.base}/education/certificate`;
@Injectable()
export class CertificateService {
constructor(private http: HttpClient) {
}
getAllUserCertificates(uuid: string): Observable<Certificate []> {
if (!uuid) {
return new Observable<Certificate []>();
}
return this.http.get<Certificate []>(`${APIURL}/${uuid}`).pipe(
tap(data => console.log("All user certificates: ", JSON.stringify(data))),
map(this.parseCertificateData),
catchError(this.handleError)
)
};
saveUserCertificates(certificates: Certificate[]): Observable<Certificate[]> {
return this.http.post<Certificate[]>(APIURL, certificates).pipe(
tap(data => console.log("All saved user certificates: ", JSON.stringify(data))),
map(this.parseCertificateData),
catchError(this.handleError)
)
};
parseCertificateData(rawCertificates: any): Certificate[] {
return Object.keys(rawCertificates).map(key => {
let certificate = new Certificate(
rawCertificates[key].model.institute,
rawCertificates[key].model.name,
rawCertificates[key].model.description,
rawCertificates[key].model.achievementDate,
rawCertificates[key].model.expirationDate,
rawCertificates[key].model.url,
rawCertificates[key].id
);
console.log(rawCertificates[key]);
return certificate;
});
};
private handleError(err: HttpErrorResponse) {
let errorMessage = "";
if (err.error instanceof ErrorEvent) {
errorMessage = `An error occurred: ${err.error.message}`;
} else {
errorMessage = `Server returned code: ${err.status}, error message is: ${err.message}`
}
console.error(errorMessage);
return throwError(errorMessage);
};
}
我写的测试如下:
import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing";
import { Certificate } from "src/app/models";
import { APICONFIG } from "src/app/config/api.config";
import { TestBed } from "@angular/core/testing";
import { CertificateService } from "../certificate.service";
describe('CertificateService', ()=>{
let httpTestingController: HttpTestingController;
const APIURL = `${APICONFIG.base}/education/certificate`;
let certificatesJson =
[
{
"id": "1",
"model": {
"institute": "Institute",
"name": "Certificaat",
"description": "Description",
"achievementDate": "2021-12-14T16:27:02.000+00:00",
"expirationDate": "2021-12-14T16:27:08.000+00:00",
"url": "url"
}
},
{
"id": "2",
"model": {
"institute": "Institute2",
"name": "Certificaat2",
"description": "Description2",
"achievementDate": "2021-12-14T16:27:02.000+00:00",
"expirationDate": "2021-12-14T16:27:08.000+00:00",
"url": "url2"
}
}
]
let certificatesParsed: Certificate[] = [{id: "1", institute: "Institute", name: "Certificaat", description: "Description", achievementDate: "2021-12-14T15:59:01.000+00:00", expirationDate: "2021-12-14T15:59:11.000+00:00", url: "url"},
{id: "2", name: "Certificaat", institute: "Institute", description: "Description", achievementDate: "2021-12-14T15:59:01.000+00:00", expirationDate: "2021-12-14T15:59:11.000+00:00", url: "url"}];
beforeEach(()=>{
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [CertificateService]
});
httpTestingController = TestBed.inject(HttpTestingController);
});
describe('getAllUserCertificates', () =>{
it('should return all certificate objects from user', () => {
let id = "1";
const certificateService = TestBed.inject(CertificateService);
certificateService.getAllUserCertificates(id).subscribe(certificates => {
expect(certificates.length).toBe(2);
});
const req = httpTestingController.expectOne(`${APIURL}/${id}`);
expect(req.request.method).toBe('GET');
req.flush(certificatesJson);
httpTestingController.verify();
});
it('should create new observable if id is null', () => {
let id = "";
const certificateService = TestBed.inject(CertificateService);
const spy = spyOn(certificateService, 'getAllUserCertificates').and.callThrough();
certificateService.getAllUserCertificates(id).subscribe();
expect(spy.calls.count()).toEqual(1);
httpTestingController.verify();
});
})
describe('saveUserCertificates', () =>{
it('should post correct certificate objects', () => {
const certificateService = TestBed.inject(CertificateService);
certificateService.saveUserCertificates(certificatesParsed).subscribe(certificates => {
expect(certificates.length).toBe(2);
});
const req = httpTestingController.expectOne(APIURL);
expect(req.request.method).toBe('POST');
req.flush(certificatesJson);
httpTestingController.verify();
});
})
describe('parseCertificateData', ()=>{
it('should parse data into certificate objects', () => {
const certificateService = TestBed.inject(CertificateService);
let certificates: Certificate[] = certificateService.parseCertificateData(certificatesJson);
expect(certificates.length).toBe(2);
expect(certificates[0].institute).toBe("Institute");
expect(certificates[0].name).toBe("Certificaat");
expect(certificates[0].description).toBe("Description");
expect(certificates[0].url).toBe("url");
expect(certificates[1].institute).toBe("Institute2");
expect(certificates[1].name).toBe("Certificaat2");
expect(certificates[1].description).toBe("Description2");
expect(certificates[1].url).toBe("url2");
});
})
describe('handleError', ()=>{
it('should handle bad request', () => {
let id = "11";
let response: any;
let errResponse: any;
const mockErrorResponse = { status: 400, statusText: 'Bad Request' };
const data = `Server returned code: 400, error message is: Http failure response for ${APIURL}/11: 400 Bad Request`;
const certificateService = TestBed.inject(CertificateService);
certificateService.getAllUserCertificates('11').subscribe(res => response = res, err => errResponse = err);
httpTestingController.expectOne(`${APIURL}/${id}`).flush(data, mockErrorResponse);
expect(errResponse).toBe(data);
});
it('should handle event error', () => {
let id = "11";
let response: any;
let errResponse: any;
const errorInitEvent: ErrorEventInit = {
error : new Error('Error'),
message : 'Error'
};
const mockErrorResponse = { status: 400, statusText: 'Bad Request'};
const data = new ErrorEvent('MyErrEventType', errorInitEvent);
const certificateService = TestBed.inject(CertificateService);
certificateService.getAllUserCertificates('11').subscribe(res => response = res, err => errResponse = err);
httpTestingController.expectOne(`${APIURL}/${id}`).flush(data, mockErrorResponse);
expect(errResponse).toBe('An error occurred: Error');
});
})
})
我的业力 console.log 目前充满了错误:
发生错误:错误
和
服务器返回代码:400,错误消息是:URL 400 Bad Request 的 Http 失败响应。
感觉就像我在使用真正的服务而不是模拟的服务。可以告诉我这样做的正确方法是什么吗?
感谢您的帮助,祝周末愉快。
不确定是什么导致测试失败,但请尝试以下操作:
在 httpTestingController = TestBed.inject(HttpTestingController);
之后的第一个 beforeEach
块中放置此行 certificateService = TestBed.inject(CertificateService);
。
在c的顶部声明certificateService
。
现在您不需要在每个 it
块中使用相同的起始代码 const certificateService = TestBed.inject(CertificateService);
。
还在 beforeEach
之后定义“全局”afterEach
块,您可以在其中执行以下操作:
httpTestingController.verify();
(又是DRY原则)。
如果有帮助,请告诉我。
我正在努力学习如何编写测试。我不明白如何从服务中模拟和测试方法。
我当前的服务文件如下:
import {Injectable} from '@angular/core';
import {HttpClient, HttpErrorResponse} from '@angular/common/http';
import {Observable, throwError} from 'rxjs';
import {catchError, map, tap} from 'rxjs/operators';
import {APICONFIG} from 'src/app/config/api.config';
import {Certificate} from 'src/app/models';
const APIURL = `${APICONFIG.base}/education/certificate`;
@Injectable()
export class CertificateService {
constructor(private http: HttpClient) {
}
getAllUserCertificates(uuid: string): Observable<Certificate []> {
if (!uuid) {
return new Observable<Certificate []>();
}
return this.http.get<Certificate []>(`${APIURL}/${uuid}`).pipe(
tap(data => console.log("All user certificates: ", JSON.stringify(data))),
map(this.parseCertificateData),
catchError(this.handleError)
)
};
saveUserCertificates(certificates: Certificate[]): Observable<Certificate[]> {
return this.http.post<Certificate[]>(APIURL, certificates).pipe(
tap(data => console.log("All saved user certificates: ", JSON.stringify(data))),
map(this.parseCertificateData),
catchError(this.handleError)
)
};
parseCertificateData(rawCertificates: any): Certificate[] {
return Object.keys(rawCertificates).map(key => {
let certificate = new Certificate(
rawCertificates[key].model.institute,
rawCertificates[key].model.name,
rawCertificates[key].model.description,
rawCertificates[key].model.achievementDate,
rawCertificates[key].model.expirationDate,
rawCertificates[key].model.url,
rawCertificates[key].id
);
console.log(rawCertificates[key]);
return certificate;
});
};
private handleError(err: HttpErrorResponse) {
let errorMessage = "";
if (err.error instanceof ErrorEvent) {
errorMessage = `An error occurred: ${err.error.message}`;
} else {
errorMessage = `Server returned code: ${err.status}, error message is: ${err.message}`
}
console.error(errorMessage);
return throwError(errorMessage);
};
}
我写的测试如下:
import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing";
import { Certificate } from "src/app/models";
import { APICONFIG } from "src/app/config/api.config";
import { TestBed } from "@angular/core/testing";
import { CertificateService } from "../certificate.service";
describe('CertificateService', ()=>{
let httpTestingController: HttpTestingController;
const APIURL = `${APICONFIG.base}/education/certificate`;
let certificatesJson =
[
{
"id": "1",
"model": {
"institute": "Institute",
"name": "Certificaat",
"description": "Description",
"achievementDate": "2021-12-14T16:27:02.000+00:00",
"expirationDate": "2021-12-14T16:27:08.000+00:00",
"url": "url"
}
},
{
"id": "2",
"model": {
"institute": "Institute2",
"name": "Certificaat2",
"description": "Description2",
"achievementDate": "2021-12-14T16:27:02.000+00:00",
"expirationDate": "2021-12-14T16:27:08.000+00:00",
"url": "url2"
}
}
]
let certificatesParsed: Certificate[] = [{id: "1", institute: "Institute", name: "Certificaat", description: "Description", achievementDate: "2021-12-14T15:59:01.000+00:00", expirationDate: "2021-12-14T15:59:11.000+00:00", url: "url"},
{id: "2", name: "Certificaat", institute: "Institute", description: "Description", achievementDate: "2021-12-14T15:59:01.000+00:00", expirationDate: "2021-12-14T15:59:11.000+00:00", url: "url"}];
beforeEach(()=>{
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [CertificateService]
});
httpTestingController = TestBed.inject(HttpTestingController);
});
describe('getAllUserCertificates', () =>{
it('should return all certificate objects from user', () => {
let id = "1";
const certificateService = TestBed.inject(CertificateService);
certificateService.getAllUserCertificates(id).subscribe(certificates => {
expect(certificates.length).toBe(2);
});
const req = httpTestingController.expectOne(`${APIURL}/${id}`);
expect(req.request.method).toBe('GET');
req.flush(certificatesJson);
httpTestingController.verify();
});
it('should create new observable if id is null', () => {
let id = "";
const certificateService = TestBed.inject(CertificateService);
const spy = spyOn(certificateService, 'getAllUserCertificates').and.callThrough();
certificateService.getAllUserCertificates(id).subscribe();
expect(spy.calls.count()).toEqual(1);
httpTestingController.verify();
});
})
describe('saveUserCertificates', () =>{
it('should post correct certificate objects', () => {
const certificateService = TestBed.inject(CertificateService);
certificateService.saveUserCertificates(certificatesParsed).subscribe(certificates => {
expect(certificates.length).toBe(2);
});
const req = httpTestingController.expectOne(APIURL);
expect(req.request.method).toBe('POST');
req.flush(certificatesJson);
httpTestingController.verify();
});
})
describe('parseCertificateData', ()=>{
it('should parse data into certificate objects', () => {
const certificateService = TestBed.inject(CertificateService);
let certificates: Certificate[] = certificateService.parseCertificateData(certificatesJson);
expect(certificates.length).toBe(2);
expect(certificates[0].institute).toBe("Institute");
expect(certificates[0].name).toBe("Certificaat");
expect(certificates[0].description).toBe("Description");
expect(certificates[0].url).toBe("url");
expect(certificates[1].institute).toBe("Institute2");
expect(certificates[1].name).toBe("Certificaat2");
expect(certificates[1].description).toBe("Description2");
expect(certificates[1].url).toBe("url2");
});
})
describe('handleError', ()=>{
it('should handle bad request', () => {
let id = "11";
let response: any;
let errResponse: any;
const mockErrorResponse = { status: 400, statusText: 'Bad Request' };
const data = `Server returned code: 400, error message is: Http failure response for ${APIURL}/11: 400 Bad Request`;
const certificateService = TestBed.inject(CertificateService);
certificateService.getAllUserCertificates('11').subscribe(res => response = res, err => errResponse = err);
httpTestingController.expectOne(`${APIURL}/${id}`).flush(data, mockErrorResponse);
expect(errResponse).toBe(data);
});
it('should handle event error', () => {
let id = "11";
let response: any;
let errResponse: any;
const errorInitEvent: ErrorEventInit = {
error : new Error('Error'),
message : 'Error'
};
const mockErrorResponse = { status: 400, statusText: 'Bad Request'};
const data = new ErrorEvent('MyErrEventType', errorInitEvent);
const certificateService = TestBed.inject(CertificateService);
certificateService.getAllUserCertificates('11').subscribe(res => response = res, err => errResponse = err);
httpTestingController.expectOne(`${APIURL}/${id}`).flush(data, mockErrorResponse);
expect(errResponse).toBe('An error occurred: Error');
});
})
})
我的业力 console.log 目前充满了错误:
发生错误:错误 和 服务器返回代码:400,错误消息是:URL 400 Bad Request 的 Http 失败响应。 感觉就像我在使用真正的服务而不是模拟的服务。可以告诉我这样做的正确方法是什么吗?
感谢您的帮助,祝周末愉快。
不确定是什么导致测试失败,但请尝试以下操作:
在 httpTestingController = TestBed.inject(HttpTestingController);
之后的第一个 beforeEach
块中放置此行 certificateService = TestBed.inject(CertificateService);
。
在c的顶部声明certificateService
。
现在您不需要在每个 it
块中使用相同的起始代码 const certificateService = TestBed.inject(CertificateService);
。
还在 beforeEach
之后定义“全局”afterEach
块,您可以在其中执行以下操作:
httpTestingController.verify();
(又是DRY原则)。
如果有帮助,请告诉我。