如何正确模拟 Angular 服务? Karma Jasmine 测试:预期间谍 service.getShipPhotos 已被调用一次。被调用0次
How to mock Angular service properly? Karma Jasmine test: Expected spy service.getShipPhotos to have been called once. It was called 0 times
我正在使用 Angular 12 和 Karma-Jasmine 4 并正在测试以验证函数 retrieveShip 正在调用函数 getSingleShip,但测试结果显示它根本没有被调用。
我怀疑正在调用真正的服务而不是模拟服务。
如何正确模拟服务?
以下依赖于 mockShipsService.getSingleShip 的类似测试失败(预期间谍 ShipsService.getSingleShip 已被调用一次。它被调用了 0 次。)。
spec.ts:
beforeEach(async () => {
const spyShipServ = jasmine.createSpyObj('ShipsService', ['getSingleShip']);
mockActivatedRoute = {
snapshot: {
paramMap: {
get: () => { return "610a80fd485a6ad03b43b539" }
}
}
}
await TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
RouterTestingModule.withRoutes(routes),
],
declarations: [
DetailedShipComponent
],
providers: [
{ provide: ShipsService, useValue: spyShipServ },
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
],
}).compileComponents();
mockShipsService = TestBed.inject(ShipsService) as jasmine.SpyObj<ShipsService>
router = TestBed.inject(Router);
});
beforeEach(() => {
fixture = TestBed.createComponent(DetailedShipComponent);
component = fixture.componentInstance;
});
describe('retrieveShip', () => {
it('should call getSingleShip once', () => {
getSingleShipResponse = {
_id: "610a80fd485a6ad03b43b539",
name: "Sabrina",
type: "cruise"
};
mockShipsService.getSingleShip.and.returnValue(of(getSingleShipResponse));
component.retrieveShip("610a80fd485a6ad03b43b539");
expect(mockShipsService.getSingleShip).toHaveBeenCalledTimes(1);
})
})
以上是根据 Angular 文档完成的:Testing services
component.ts:
retrieveShip(shipId: string): void {
this.shipsService.getSingleShip(shipId).subscribe({
next: response => {
this.ship = response;
},
error: err => this.errorMessage = err
});
}
service.ts:
@Injectable({
providedIn: 'root'
})
export class ShipsService {
readonly ROOT_URL: string = environment.ROOT_URL;
constructor(
private http: HttpClient,
) {
}
getSingleShip(shipId: string): Observable<Ship> {
return this.http.get<Ship>(`${this.ROOT_URL}/ships/${shipId}`);
}
}
如有任何提示,我们将不胜感激!
通常您会提供一个模拟服务(如果像这样的服务执行 HTTP 调用,或者不与使用相同服务的其他单元测试混在一起,则不会产生真正的后果)并提供该模拟。然后你监视那个模拟,创建一个对象或只是间谍,并给它你期望的 return 值。我会说,你是 95% 正确。
还记得 fakeAsync 和 tick() 异步调用。如果你 return of(element) (又名可观察或承诺)它不会同步计算调用。
例如:
const spyShipServ: Partial<ShipsService> = {
public getSingleShip(shipId: string): Ship { return null; }
describe('retrieveShipComponent', () => {
it('should call getSingleShip once', fakeAsync(() => {
//you're not doing anything with the return, so and.returnValue isn't necessary here
spyOn(spyShipServ, 'getSingleShip');
component.retrieveShip("610a80fd485a6ad03b43b539");
tick();
expect(spyShipServ.getSingleShip).toHaveBeenCalledTimes(1);
))}
}
我得出以下解决方案:注入真实服务并使用 callFake()。
spec.ts:
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
],
declarations: [
DetailedShipComponent
],
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DetailedShipComponent);
component = fixture.componentInstance;
service = fixture.debugElement.injector.get(ShipsService);
});
describe('retrieveShip', () => {
it('should call getSingleShip once', waitForAsync(() => {
spyOn(service, "getSingleShip").and.callFake(() => {
return of(singleShipResponse);
})
component.retrieveShip("610a80fd485a6ad03b43b539");
expect(service.getSingleShip).toHaveBeenCalledTimes(1);
}))
})
测试现在通过了!
这是让我产生想法的视频:
https://www.youtube.com/watch?v=l4oYN3TvKM4
我正在使用 Angular 12 和 Karma-Jasmine 4 并正在测试以验证函数 retrieveShip 正在调用函数 getSingleShip,但测试结果显示它根本没有被调用。 我怀疑正在调用真正的服务而不是模拟服务。 如何正确模拟服务?
以下依赖于 mockShipsService.getSingleShip 的类似测试失败(预期间谍 ShipsService.getSingleShip 已被调用一次。它被调用了 0 次。)。
spec.ts:
beforeEach(async () => {
const spyShipServ = jasmine.createSpyObj('ShipsService', ['getSingleShip']);
mockActivatedRoute = {
snapshot: {
paramMap: {
get: () => { return "610a80fd485a6ad03b43b539" }
}
}
}
await TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
RouterTestingModule.withRoutes(routes),
],
declarations: [
DetailedShipComponent
],
providers: [
{ provide: ShipsService, useValue: spyShipServ },
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
],
}).compileComponents();
mockShipsService = TestBed.inject(ShipsService) as jasmine.SpyObj<ShipsService>
router = TestBed.inject(Router);
});
beforeEach(() => {
fixture = TestBed.createComponent(DetailedShipComponent);
component = fixture.componentInstance;
});
describe('retrieveShip', () => {
it('should call getSingleShip once', () => {
getSingleShipResponse = {
_id: "610a80fd485a6ad03b43b539",
name: "Sabrina",
type: "cruise"
};
mockShipsService.getSingleShip.and.returnValue(of(getSingleShipResponse));
component.retrieveShip("610a80fd485a6ad03b43b539");
expect(mockShipsService.getSingleShip).toHaveBeenCalledTimes(1);
})
})
以上是根据 Angular 文档完成的:Testing services
component.ts:
retrieveShip(shipId: string): void {
this.shipsService.getSingleShip(shipId).subscribe({
next: response => {
this.ship = response;
},
error: err => this.errorMessage = err
});
}
service.ts:
@Injectable({
providedIn: 'root'
})
export class ShipsService {
readonly ROOT_URL: string = environment.ROOT_URL;
constructor(
private http: HttpClient,
) {
}
getSingleShip(shipId: string): Observable<Ship> {
return this.http.get<Ship>(`${this.ROOT_URL}/ships/${shipId}`);
}
}
如有任何提示,我们将不胜感激!
通常您会提供一个模拟服务(如果像这样的服务执行 HTTP 调用,或者不与使用相同服务的其他单元测试混在一起,则不会产生真正的后果)并提供该模拟。然后你监视那个模拟,创建一个对象或只是间谍,并给它你期望的 return 值。我会说,你是 95% 正确。
还记得 fakeAsync 和 tick() 异步调用。如果你 return of(element) (又名可观察或承诺)它不会同步计算调用。
例如:
const spyShipServ: Partial<ShipsService> = {
public getSingleShip(shipId: string): Ship { return null; }
describe('retrieveShipComponent', () => {
it('should call getSingleShip once', fakeAsync(() => {
//you're not doing anything with the return, so and.returnValue isn't necessary here
spyOn(spyShipServ, 'getSingleShip');
component.retrieveShip("610a80fd485a6ad03b43b539");
tick();
expect(spyShipServ.getSingleShip).toHaveBeenCalledTimes(1);
))}
}
我得出以下解决方案:注入真实服务并使用 callFake()。
spec.ts:
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
],
declarations: [
DetailedShipComponent
],
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DetailedShipComponent);
component = fixture.componentInstance;
service = fixture.debugElement.injector.get(ShipsService);
});
describe('retrieveShip', () => {
it('should call getSingleShip once', waitForAsync(() => {
spyOn(service, "getSingleShip").and.callFake(() => {
return of(singleShipResponse);
})
component.retrieveShip("610a80fd485a6ad03b43b539");
expect(service.getSingleShip).toHaveBeenCalledTimes(1);
}))
})
测试现在通过了!
这是让我产生想法的视频: https://www.youtube.com/watch?v=l4oYN3TvKM4