当流/事件具有管道(takeUntil(destroy))时,单元测试失败



角度:9.1.13;RxJS:6.6.3;因果报应:4.3.0;茉莉花心:2.6.2

我们有一个组件,它有一个处理流和事件的服务。

当流/事件通过调用this._destroy.next()被破坏时,每个这样的组件方法都会取消订阅,在ngOnDestroy钩子中,subject声明为private _destroy = new Subject()

在我们运行单元测试之前,一切似乎都很好。方法和测试示例如下。

目前尚不清楚在订阅具有takeUntil的服务流/事件时测试失败的原因。

(另一个用例(不使用返回具有pipe(takeUntil(destroy))的可观察性的存根

使用jasmine/carma和phantom.js 运行测试时出错

TypeError: undefined is not a constructor (evaluating 'this.service.getServicePackages().pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_9__["takeUntil"])(this._destroy))')

方法示例:

getServicePackages(): void {
this.service.getServicePackages().pipe(takeUntil(this._destroy)).subscribe(() => ... );
}

测试示例:


beforeAll(() => {
serviceStub = {
getServicePackages: () => ({ subscribe: () => ({}) }),
// v2 -> getServicePackages: () => ({ subscribe: (): Observable<TYPE> => of({}) }),
}
})
it('...', () => {
const serviceStub = TestBed.inject(PackageService);
spyOn(serviceStub, 'getServicePackage').and.callThrough();
spyOn(component, 'getServicePackage').and.callThrough();
component.getServicePackage();
expect(component.getServicePackage).toHaveBeenCalled();
expect(serviceStub.getServicePackage).toHaveBeenCalled();
})

如果我们将两条线放在上,问题就会变得明显

serviceStub = {
getServicePackages: () => ({ subscribe: () => ({}) }),
// v2 -> getServicePackages: () => ({ subscribe: (): Observable<TYPE> => of({}) }),
};
// and then in code of component
serviceStub.getServicePackages().pipe(does not matter what); // here it doesn't have pipe method

我建议返回可观察到的物体本身。如果您还想测试事件,请返回一个主题。

servicePackagesSubject = new Subject();
serviceStub = {
getServicePackages: jasmine.createSpy('getServicePackages').and.returnValue(servicePackagesSubject),
}

我认为这是处理以下情况的最佳方法:

// As mentioned earlier by @Andrei, it does not matter about the pipe operators. 
// In your test case, create a mock service, return a value, and use it as a useClass method in the providers and simply spyOn your component method. Something like this: 

// In your spec file, 
@Injectable() 
class MockService extends YourOriginalService {
getServicePackages() {
// Since you are subscribing, i am assuming this returns an observable, hence
// the 'of' operator

const mockData = {fill your mockData here};
return of(mockData);
}
}
// In your before each, add the mockService to your providers 
let mockService: YourOriginalService;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [],
....
providers: [
{
provide: YourOriginalService,
useClass: MockService
}
]
......
}).compileComponents();
mockService = TestBed.get(YourOriginalService);
}));

/// Now your test case turns to this: 
it('should call the #getServicePackages() method in component', () => {

spyOn(component, 'getServicePackages').and.callThrough();
component.getServicePackages();
expect(component.getServicePackages).toHaveBeenCalled();
});

// Now if you want to test the 'error' part in the subscribe, you can take care of that in this test case like this: 
it('should call the #getServicePackages() method in component, for error scenario', () => {
// Assuming its an API Call, otherwise replace the status object with your mock error object
spyOn(component, 'getServicePackages').and.callThrough();
spyOn(mockService, 'getServicePackages')
.and.returnValue(throwError(of({status: 500})));
component.getServicePackages();
expect(component.getServicePackages).toHaveBeenCalled();
});
// Note: Please don't mix the service success scenario coverage and service error scenario coverage in the same test case. Use 2 test cases like above. 

最新更新