9

比方说,我有一个订阅服务功能的组件:测试误差的情况下,在服务观测

export class Component { 

    ... 

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

的订阅调用需要两个匿名函数作为参数,我已经成功地建立了一个工作单元测试数据功能,但Karma不会接受错误1的覆盖范围。

enter image description here

我试图刺探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. 
    } 
); 

什么是用于测试的匿名功能,如这些最佳做法?确保测试覆盖率的最低标准是多少?

+1

你的测试没有意义;你正在替换你说你想测试的回调。只要测试'subscribe'在发生错误时调用其第二个参数就不会测试您的代码,它就会测试框架,这是一种反模式。你也可以为连续线上的模拟呼叫提供两种不同的结果。 – jonrsharpe

+0

好的,那么测试它的好方法是什么?在单独的块中抛出一个错误? – user3656550

+1

你需要调用实际的实现,但是你没有足够的[mcve]来猜测如何实现。 – jonrsharpe

回答

7

不确定您正在显示的代码的目的是试图测试模拟服务。覆盖问题与组件和错误回调没有被调用(只有在出现错误时才调用)。

我通常为我的大多数可观察服务所做的工作就是创建一个模拟方法,它的方法只是返回自己。模拟服务有一个subscribe方法,它接受next,errorcomplete回调。模拟的用户可以配置它以添加错误,以便调用error函数或添加数据,以便调用next方法。我最喜欢的就是它都是同步的。

下面就是我通常使用的东西。这只是其他模拟的抽象类来扩展。它提供了observable提供的基本功能。扩展模拟服务应该只添加它需要的方法,并在方法中返回自己。

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(); 
}) 
8

你可以像可观察Observable.throw({status: 404})和测试误差块只是模拟可观察扔错误对象。

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