I'm struggling to get a unit test around an Observable to behave the way I expect. I'm using an Observable essentially just to notify that a process has pleted. There's no async code in the loggout function yet, but there will be when I implement all the logic. I don't need the observable to return any data, I just want it to invoke the subscriber as a signal that the logout is plete.
The problem is my Unit Tests revealed the subscriber is never actually invoked. I normally work with async/await, ironically because Observables tend to give me headaches by doing stuff like this, but I'm trying to get better at the front-end, which seems to prefer Observables.
This is a new Angular 9 application that I'm scaffolding up. Unit tests are running in Jest, and I'm running them through Wallaby.
Here's my troubleshooting:
I deliberately broke the function to verify my my unit test works.
The grey box on line 63 of my tests is Wallaby indicating that the test never executed that line. So naturally the test passed because the assertion was never checked.
If I change the observable to return a value I at least get the line to execute... sort of.
But I'm not getting getting the expected failure message from my mock. It should report "expected clearSession() to be called 1 times, was 0"
So I run Jest just to see what's going on and I get:
FAIL src/app/auth/auth.service.spec.ts
● AuthService › logout › should destroy the session
1 timer(s) still in the queue.
at node_modules/zone.js/dist/fake-async-test.js:621:31
at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:386:30)
at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/proxy.js:117:43)
at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:385:36)
at Zone.Object.<anonymous>.Zone.run (node_modules/zone.js/dist/zone.js:143:47)
To make things really strange... if I fix the function everything is happy! But only so long as the Observable is returning a value, even though I'm only using the Observable to notify when the process is plete. But I haven't been able to get my actual assertion to fail, it failed because of something seemingly unrelated (The timer in queue? Whatever that means).
The verify
function is from ts-mockito
and works essentially like the expect(someSpy).toHaveBeenCalledTimes()
function.
Is there some quirk about Observables that I'm missing?
I'm struggling to get a unit test around an Observable to behave the way I expect. I'm using an Observable essentially just to notify that a process has pleted. There's no async code in the loggout function yet, but there will be when I implement all the logic. I don't need the observable to return any data, I just want it to invoke the subscriber as a signal that the logout is plete.
The problem is my Unit Tests revealed the subscriber is never actually invoked. I normally work with async/await, ironically because Observables tend to give me headaches by doing stuff like this, but I'm trying to get better at the front-end, which seems to prefer Observables.
This is a new Angular 9 application that I'm scaffolding up. Unit tests are running in Jest, and I'm running them through Wallaby.
Here's my troubleshooting:
I deliberately broke the function to verify my my unit test works.
The grey box on line 63 of my tests is Wallaby indicating that the test never executed that line. So naturally the test passed because the assertion was never checked.
If I change the observable to return a value I at least get the line to execute... sort of.
But I'm not getting getting the expected failure message from my mock. It should report "expected clearSession() to be called 1 times, was 0"
So I run Jest just to see what's going on and I get:
FAIL src/app/auth/auth.service.spec.ts
● AuthService › logout › should destroy the session
1 timer(s) still in the queue.
at node_modules/zone.js/dist/fake-async-test.js:621:31
at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:386:30)
at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/proxy.js:117:43)
at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:385:36)
at Zone.Object.<anonymous>.Zone.run (node_modules/zone.js/dist/zone.js:143:47)
To make things really strange... if I fix the function everything is happy! But only so long as the Observable is returning a value, even though I'm only using the Observable to notify when the process is plete. But I haven't been able to get my actual assertion to fail, it failed because of something seemingly unrelated (The timer in queue? Whatever that means).
The verify
function is from ts-mockito
and works essentially like the expect(someSpy).toHaveBeenCalledTimes()
function.
Is there some quirk about Observables that I'm missing?
Share Improve this question asked Apr 26, 2020 at 18:32 GridDragonGridDragon 3,1453 gold badges35 silver badges44 bronze badges2 Answers
Reset to default 17I posted this question to Reddit last night. Just got an answer there that solved this, so posting here for anyone trying to solve the same.
Change of()
to of(undefined)
Change of() to of(undefined) works for me
it('should call myCall', fakeAsync(() => {
myService = TestBed.inject(MyService);
const fakeRes: Observable<void> = of(undefined);
spyOn(myService , 'myCall').and.callFake(() => fakeRes);
tick();
fixture.detectChanges();
ponent.myMethod();
expect(myService.myCall).toHaveBeenCalled();
}));