Reputation: 213
I have a problem trying to unit test an angular service. I want to verify that this service is properly calling another service that is injected into it.
Lets say I have this ServiceToTest that injects ServiceInjected:
ServiceToTest .service.ts
@Injectable()
export class ServiceToTest {
constructor(private _si: ServiceInjected) {}
public init() {
this._si.configure();
}
}
ServiceInjected.service.ts
@Injectable()
export class ServiceInjected {
constructor() {}
public configure() {
/*Some actions*/
}
}
With these services, now I write my unit test:
const serviceInjectedStub = {
configure(): void {}
}
describe('ServiceToTest service Test', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [ServiceToTest ,
{ provide: ServiceInjected, useValue: serviceInjectedStub }]
});
});
it('should be initialize the service injected', inject([ServiceToTest],
(tService: ServiceToTest) => {
spyOn(serviceInjectedStub, 'configure');
tService.init();
expect(serviceInjectedStub.configure).toHaveBeenCalled();
}));
I expected my test to be positive, however I receive the following error:
Expected spy configure to have been called.
On the other hand, it works OK if I set the injected service public in this way:
private _si: ServiceInjected by public si: ServiceInjected
Upvotes: 21
Views: 28507
Reputation: 894
From Angular 9 you should use TestBed.inject
; then you don't need to add the service on providers' list.
let injectedService: ServiceInjected;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [ServiceToTest]
});
injectedService = TestBed.inject(ServiceInjected);
});
Then spy on it
// if you want to ignore the call
spyOn(injectedService, 'configure').and.callThrough();
// if you need to set stuff up
spyOn(injectedService, 'configure').and.callFake(async () => {/* some fake stuff */});
And do the checks
expect(injectedService.configure).toHaveBeenCalled();
// ...
Upvotes: 2
Reputation: 5832
Or you can use jasmine.createSpyObj
and provide it with useValue
like bellow:
describe('YourComponent', () => {
let serviceInjectedSpy: jasmine.SpyObj<ServiceInjected>;
beforeEach(async(() => {
// notice here
serviceInjectedSpy = jasmine.createSpyObj('ServiceInjected', ['configure']);
TestBed.configureTestingModule({
declarations: [YourComponent],
providers: [
{provide: ServiceInjected, useValue: serviceInjectedSpy}
],
imports: [
...
]
}).compileComponents().then(() => {
fixture = TestBed.createComponent(YourComponent);
component = fixture.componentInstance;
});
});
it('should assert my test', () => {
serviceInjectedSpy.configure.and.returnValue(/* what you want */);
component.init();
expect(serviceInjectedSpy.configure).toHaveBeenCalled();
});
});
Upvotes: 9
Reputation:
You don't spy on the service tied to your TestBed. Get the service from your Testbed
beforeEach(() => {
TestBed.configureTestingModule({
providers: [ServiceToTest ,
{ provide: ServiceInjected, useValue: serviceInjectedStub }]
});
injectedService = TestBed.get(ServiceInjected);
});
And test on it
spyOn(injectedService, 'configure').and.returnValue(/* return same data type here */);
// ...
expect(injectedService.configure).toHaveBeenCalled();
Upvotes: 47
Reputation: 3740
Use this:
spyOn(serviceInjectedStub, 'configure').and.returnValue(config); // config is a mock
Upvotes: 1