簡體   English   中英

Angular單元測試結合最新

[英]Angular unit test combineLatest

我已經開始在 Angular 中使用 Jest Framework 進行單元測試。 但是,我陷入了需要對combineLatest RxJS 運算符進行單元測試的情況。 我的組件如下所示。

零件:

public userData;
public productData;

constructor(
    private readonly userService: UserService,
    private readonly productService: ProductService
) {}

public ngOnInit() {
    this.initialize();
}

public initialize() {
    combineLatest([
        this.userService.getUserData(),
        this.productService.getProductData()
    ])
    .subscribe([userData, productData] => {
        this.userData = userData;
        this.productData = productData;
    });
}

我已經嘲笑了我的服務,我的單元測試如下所示。

it('should initialize user and product data', fakeAsync(() => {
    spyOn(userService, 'getUserData');
    spyOn(productService, 'getProductData');
    component.initialize();
    tick();
    fixture.detectChanges();
    expect(userService.getUserData).toHaveBeenCalled();
    expect(productService.getProductData).toHaveBeenCalled();
    expect(component.userData).toBeDefined();
    expect(component.productData).toBeDefined();
}));

此測試失敗,收到:未定義。 但是在使用單個 observable 時,同樣的測試也有效。

我將嘗試以不同的方式回答這個問題。

讓我們看看你的代碼,測試簡單的代碼塊比測試長代碼更容易。

將變量分配給您的屬性會容易得多。 有了它,您可以簡單地測試屬性

請參閱下面的改進代碼;

constructor(
    private readonly userService: UserService,
    private readonly productService: ProductService
) {}
userData: any;
productData: any;
userData$ = this.userService.getUserData().pipe(
  tap(userData => this.userData = userData)
);
productData$ = this.productService.getProductData().pipe(
  tap(productData = productData => this.productData = productData)
)

v$ = combineLatest([this.userData$, this.productData$]).pipe(
  map(([userData, productData]) => ({ userData, productData }))
)
initialize() {
  v$.subscribe()
}
ngOnInit() {
    this.initialize();
}

回到你最初的測試

現在讓我們嘗試測試您的初始代碼並了解為什么測試不起作用。 來自官方文檔

當任何 observable 發出一個值時,從每個 observable 發出最后發出的值

讓我們看看下面的代碼

    spyOn(userService, 'getUserData');
    spyOn(productService, 'getProductData');

上面的代碼是否發出任何值? 答案是否定的,因此代碼this.userService.getUserData()this.productService.getProductData()不會返回任何值,因此不會發出任何值。 因此combineLatest不會發出任何值。

解決方案

您需要從spyOn()函數返回一個Observable ,類似於 spyOn(userService, 'getUserData').and.returnValue(of({}));

現在 Observable 將發出並且測試將通過

一個更好的代碼的最終建議。

您可以考慮使用async管道來處理您的訂閱。 使用async管道,您的代碼可以如下編寫

constructor(
    private readonly userService: UserService,
    private readonly productService: ProductService
) {}
userData$ = this.userService.getUserData()
productData$ = this.productService.getProductData()
v$ = combineLatest([this.userData$, this.productData$]).pipe(
  map(([userData, productData]) => ({ userData, productData }))
)

在你的 html

<ng-container *ngIf='v$ | async'>
  {{ v.userData | json }}
  {{ v.productData| json }}   
</ng-container>

在您的測試文件中,您可以擁有

it('should initialize user and product data', fakeAsync(() => {
    spyOn(userService, 'getUserData').and.returnValue(of({}));
    spyOn(productService, 'getProductData').and.returnValue(of({}));
    tick();
    fixture.detectChanges();
    component.v$.subscribe({
      next: (v) => {
        expect(v.userData).toBeDefined();
        expect(v.productData).toBeDefined();
      }
    })
}));

上面的測試只是檢查是否我們訂閱了v$ ,然后定義了userDataproductData

你可以像這樣jest地模擬combineLatest

/* Mock rxjs's combineLatest */
const rxjs = jest.requireActual('rxjs');
rxjs.combineLatest = jest.fn(() => of(<ADD_YOUR_MOCK_DATA_HERE>));

完整測試看起來像這樣

it('should initialize user and product data', () => {
  const userMockData = <ADD_MOCK_DATA>;
  const productMockData = <ADD_MOCK_DATA>;
  
  /* Mock rxjs's combineLatest */
  const rxjs = jest.requireActual('rxjs');
  rxjs.combineLatest = jest.fn(() => of([userMockData, productMockData]));

  jest.spyOn(userService, 'getUserData');
  jest.spyOn(productService, 'getProductData');

  component.initialize();

  expect(component.userData).toEqual(userMockData);
  expect(component.productData).toEqual(productMockData);
});

暫無
暫無

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

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