Проверка ошибки с наблюдаемыми в сервисах

Предположим, у меня есть компонент, который подписывается на служебную функцию:

export class Component { ... ngOnInit() { this.service.doStuff().subscribe( (data: IData) => { doThings(data); }, (error: Error) => console.error(error) ); }; }; 

Вызов подписывается на две анонимные функции в качестве параметров, мне удалось настроить тестовый тест для функции данных, но Karma не примет покрытие для ошибки.

введите описание изображения здесь

Я пробовал отслеживать функцию console.error, вызывая ошибку и ожидая, что шпион будет вызван, но это не совсем так.

Мой модульный тест:

 spyOn(console,'error').and.callThrough(); serviceStub = { doStuff: jasmine.createSpy('doStuff').and.returnValue(Observable.of(data)), }; serviceStub.doStuff.and.returnValue(Observable.throw( 'error!' )); serviceStub.doStuff().subscribe( (res) => { *working test, can access res* }, (error) => { console.error(error); console.log(error); //Prints 'error!' so throw works. expect(console.error).toHaveBeenCalledWith('error!'); //Is true but won't be accepted for coverage. } ); 

Какова наилучшая практика для тестирования анонимных функций, таких как эти? Каков минимальный уровень защиты тестового покрытия?

    Вы можете просто высмеять объект ошибки Observable throw, такой как Observable.throw({status: 404}) и проверочный блок ошибок наблюдаемого.

     const xService = fixture.debugElement.injector.get(SomeService); const mockCall = spyOn(xService, 'method') .and.returnValue(Observable.throw({status: 404})); 

    Непосредственно укажите цель кода, который вы показываете, который пытается протестировать макет службы. Проблема покрытия связана с тем, что компонент и обратный вызов ошибки не были вызваны (который вызывается только при возникновении ошибки).

    То, что я обычно делаю для большинства моих наблюдаемых сервисов, заключается в создании макета, методы которого просто возвращаются сами. У макетной службы есть метод subscribe который принимает next , error и complete обратные вызовы. Пользователь макета получает, чтобы настроить его, чтобы добавить ошибку, чтобы функция error вызывалась или добавляла данные, поэтому вызывается next метод. Мне больше всего нравится то, что все синхронно.

    Ниже что-то вроде того, что я обычно использую. Это просто абстрактный class для других макетов. Он обеспечивает базовую функциональность, которую обеспечивает наблюдаемое. Расширяющаяся макетная служба должна просто добавлять методы, которые ей нужны, возвращаясь в методе.

     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; } } 

    Теперь в ваших тестах вы просто делаете что-то вроде

     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(); }) 
    Давайте будем гением компьютера.