I will try and answer the question in a different approach.
Lets take a look at your code, It is easier to test simple chunks of code rather that test long code.
It will be much easier to assign the variables to your properties. With that you can simply test the property
See Below improved code;
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();
}
Back to your initial testing
Lets now try to test your initial code and understand why the test is not working.
From the official documentation
When any observable emits a value, emit the last emitted value from each
Lets have a look at the below code
spyOn(userService, 'getUserData');
spyOn(productService, 'getProductData');
Does the above code emit any value? The answer is no, so the code this.userService.getUserData()
and this.productService.getProductData()
will not return any value hence no value will be emitted. combineLatest
will hence not emit any value.
Solution
You will need to return an Observable
from the spyOn()
function, something like
spyOn(userService, 'getUserData').and.returnValue(of({}));
Now the Observable will emit and the test will pass
Final proposal for a better code.
You may consider using async
pipe to handle your subscription. With async
pipe your code can be written as below
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 }))
)
In your html
<ng-container *ngIf='v$ | async'>
{{ v.userData | json }}
{{ v.productData| json }}
</ng-container>
In your test file you can have
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();
}
})
}));
The above test simply checks that if we subscribe to v$
, then the userData
and productData
are defined
TypeError: Cannot set property combineLatest of [object Object] which has only a getter
– Gristle