Not sure exactly the purpose of the code you are showing, which is trying to test a mock service. The coverage problem is with the component and the error callback to not have been called (which is only called when there is an error).
What I usually do for most of my observable services, is to create a mock whose methods just returns itself. The mock service has a subscribe
method that accepts the next
, error
, and complete
callbacks. The user of the mock gets to configure it to add an error so the error
function gets called, or add data, so the next
method gets called. The thing I like most about this is that it's all synchronous.
Below is something like what I normally use. It's just an abstract class for other mocks to extend. It provides the basic functionality that an observable provides. The extending mock service should just add the methods it needs, returning itself in the method.
import { Subscription } from 'rxjs/Subscription';
export abstract class AbstractMockObservableService {
protected _subscription: Subscription;
protected _fakeContent: any;
protected _fakeError: any;
set error(err) {
this._fakeError = err;
}
set content(data) {
this._fakeContent = data;
}
get subscription(): Subscription {
return this._subscription;
}
subscribe(next: Function, error?: Function, complete?: Function): Subscription {
this._subscription = new Subscription();
spyOn(this._subscription, 'unsubscribe');
if (next && this._fakeContent && !this._fakeError) {
next(this._fakeContent);
}
if (error && this._fakeError) {
error(this._fakeError);
}
if (complete) {
complete();
}
return this._subscription;
}
}
Now in your tests you just do something like
class MockService extends AbstractMockObservableService {
doStuff() {
return this;
}
}
let mockService;
beforeEach(() => {
mockService = new MockService();
TestBed.configureTestingModule({
providers: [{provide: SomeService, useValue: mockService }],
declarations: [ TestComponent ]
});
});
it('should call service success', () => {
mockService.content = 'some content';
let fixture = TestBed.createComponent(TestComponent);
// test component for success case
});
it('should call service error', () => {
mockService.error = 'Some error';
let fixture = TestBed.createComponent(TestComponent);
// test component for error case
// this should handle your coverage problem
});
// this assumes you have unsubscribed from the subscription in your
// component, which you should always do in the ngOnDestroy of the component
it('should unsubscribe when component destroyed', () => {
let fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
fixture.destroy();
expect(mockService.subscription.unsubscribe).toHaveBeenCalled();
})
subscribe
calls its second argument when an error occurs isn't testing your code, it's testing the framework, which is an anti pattern. Also you provide two different outcomes for your mock call on consecutive lines. – Hairstreak