簡體   English   中英

當流/事件有管道時單元測試失敗(takeUntil(destroy))

[英]Unit test fails when stream/event has pipe(takeUntil(destroy))

Angular:9.1.13; RxJS:6.6.3; 業力:4.3.0; 茉莉花核:2.6.2

我們有一個具有服務的組件,它對流和事件做一些事情。

每個這樣的組件方法在通過調用this._destroy.next()被銷毀時取消訂閱流/事件,在ngOnDestroy鈎子中聲明為private _destroy = new Subject()的主題。

在我們運行單元測試之前,一切似乎都很好。 方法和測試示例可以在下面找到。

目前尚不清楚為什么在使用takeUntil訂閱服務流/事件時測試失敗。

(另一個用例)不使用返回具有pipe(takeUntil(destroy))

使用 jasmine/karma 和 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();
})

如果我們將 2 行放在一起,問題就會變得很明顯

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

我建議返回 observable 本身。 如果您還想測試事件 - 返回一個主題。

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. 


暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM