[英]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$
,然后定義了userData
和productData
你可以像這樣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.