Let's say I have an Angular Component which defines an Observable myObs$
as one of its properties.
In one test, given certain conditions, I want to test that myObs$
does not notify. In the logic there are some delays involved, so the test must be asynchronous.
I am using Jasmine.
So far I have been able to craft this solution:
it('async test', done => {
let dataEmitted;
myObs$
.pipe(
tap(data => dataEmitted = data),
)
.subscribe();
setTimeout(() => {
if (dataEmitted) {
done.fail('should not emit');
} else {
done();
}
}, 1000);
});
But I am far from happy with it. I have to rely on setTimeout
to perform the checks and call the done
function.
Any suggestions on how to perform such tests properly? Synchronous solutions do not work since there is intrinsic asynchronicity in the logic.
An observable produces values over time. An array is created as a static set of values. In a sense, observables are asynchronous where arrays are synchronous.
detectChanges(). Delayed change detection is intentional and useful. It gives the tester an opportunity to inspect and change the state of the component before Angular initiates data binding and calls lifecycle hooks.
Testing a Single Emitted Value You can then write your expectations inside of the the subscribe callback, then call done() when you're ready for the test to finish. import { of } from 'rxjs'; test('the observable emits hello', done => { of('hello'). subscribe( data => { expect(data). toBe('hola'); done(); }); });
If it's asynchronous logic that based on setTimeout/debounceTime and so forth, you can use fakeAsync() function to test it, this function will substitute all these asynchronous operations with synchronous ones, so it will be possible to test your logic since it's synchronous. Also you can use tick() to skip VM turns (and it happens also synchronously!). Using this solution you will have nice and clean, fast and reliable unit tests.
it('asynch test', fakeAsync(() => {
let dataEmitted;
myObs$.subscribe(data => dataEmitted = data);
tick(1000);
expect(dataEmitted).toBeUndefined();
}));
I suggest you also to check negative scenario, for example tick(2000) and check if it emits value. Hope that helps.
Assuming that the Observable eventually completes, this can also be done with the conventional it
function by using done()
:
it(
'async test',
(done: DoneFn) => {
let emitted = false;
myObs$
.subscribe({
next: () => emitted = true;
complete: () => {
expect(emitted).toBeFalse();
done();
}
});
}
);
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