I am trying to test a service that is using a BehaviourSubject
, however I am not exactly sure how. Here is my service:
export class ProductService {
private changes$: BehaviorSubject<User[]> = new BehaviorSubject([]);
get products() { return this.changes$.asObservable() as Observable<any>; }
getProducts() {
this.http.get(...)
.subscribe((products) => {
this.changes$.next(products);
}
}
filterById(id: number) {
let products = this.products$.getValue().filter(...);
// Maybe I will have some more filter logic here.
// I want to test that the filter logic is returning the correct product
this.changes$.next(products);
}
...
}
Here is my test. I want to be able to test the filter logic of my service. But since I am mocking the data, this.product$.getValue()
will be empty and the filter will not run.
it('should filter the products and return the correct product)',
inject([ProductService], (service: ProductService) => {
const usersSpy = spyOnProperty(service, 'products', 'get').and.returnValue(Observable.of(mockGetResponse));
service.products.subscribe((res) => {
// How do I continue here?
// the `this.products$.getValue()`
// will not contain any items,
// because I am mocking the data.
service.findByUserId(1);
});
}));
When you're testing an observable sequence, it's important to keep in mind that the emitted values come later (asynchronously) (JavaScript is single-threaded, but you can treat it similarly to a separate thread). So, there's nothing explicitly wrong with trying to test them with mock data.
BehaviorSubject
is a bit of a corner case, as it is programmed to emit some type of default value when subscribe()
is called. Thus, you probably want to skip this value the first time around.
it('should emit filtered products', async(inject([ProductService], (service: MockProductService) => {
let testId = 'testId'; // you need to define this as appropriate
let testProduct; // you need to define this to be something that will be returned when the filter is set
// subscribe, but skip the first as this is a BehaviorSubject and emits a default first value.
service.filteredProducts$.skip(1).subscribe((o) => expect(o).toBe(testProduct));
service.findByUserId(testId);
})));
Writing a spy in this case doesn't seem to make a whole lot of sense, as all you're doing is testing the test code. If you're trying to test the service itself, you can stub the methods that reach back to the server, or you can write a mock component (which is what I usually do) that does this (and I usually make my mock component and real component share a common base class).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With