1
@Injectable({
  providedIn: 'root'
})
export class MyResolver implements Resolve<LoggedUserProfile> {
   constructor(private readonly _store: Store<AppState>) {}

   resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
   ): Observable<LoggedUserProfile> {

   return this._store.pipe(
      select(selectLoggedUserProfile),
      filter(profile => profile && !!profile.id),
      take(1)
   );
  }
}

I want to test that the route is not resolved when the profile is not a valid object. For that, I am checking that the function take is not called.

import * as rxjsOperatores from 'rxjs/operators';

it('should not resolve the route if profile is not valid', () => {
   const setPropertyTypeSpy = jasmine.createSpy('take');
   const takeSpyOn = spyOnProperty(rxjsOperatores, 'take').and.returnValue(
     setPropertyTypeSpy
   );

   mockStore.overrideSelector(selectLoggedUserProfile, null);

   service.resolve(route, null).subscribe(() => {
      expect(takeSpyOn).not.toHaveBeenCalled();
      done();
   });
});

Spying on "take" has been a challenge for hours now.

First, I tried to spy on the take method like this

const takeSpyOn = spyOn(rxjsOperatores, 'take');

I got an error saying:

Error: <spyOn> : take is not declared writable or has no setter

Then I tried the below technique as suggested by certain people online

const setPropertyTypeSpy = jasmine.createSpy('take');
const takeSpyOn = spyOnProperty(rxjsOperatores, 'take').and.returnValue(
 setPropertyTypeSpy
);

Then I get the following error:

TypeError: fn is not a function

Is there a way to spy on the rxjs operators?

I've tried this technique and this other too but didn't work either.

Thanks for helping

Richard77
  • 20,343
  • 46
  • 150
  • 252

1 Answers1

0

I found another Q&A about testing observables that don't emit anything (which is the purpose of your test).

You can use the isEmpty() operator in your test to check whether nothing is emitted when your profile is invalid.

service.resolve(route, null)
.pipe(isEmpty())
.subscribe(isEmpty => {
  expect(isEmpty).toBeTrue();
  done();
});

If the test fails, this means take() is emitting something after your filter(). If the test passes, it means your filter() prevented any value to be emitted from `take().

Reference: How to test if returned Observable is EMPTY

Joshua McCarthy
  • 1,739
  • 1
  • 9
  • 6
  • It looks like there's a difference with the case you provided the link for. In your link, the method returns an `EMPTY observable`. That's why it's possible to check using the `sEmpty()` method. – Richard77 Sep 09 '21 at 03:50
  • My case is different, I am using a `filter()` operator, which suspends the execution until the condition is satisfied or the test just times out. – Richard77 Sep 09 '21 at 03:51
  • Do you have a guard in place for the route that's using this resolver? – Joshua McCarthy Sep 09 '21 at 06:01
  • `Yes`. This resolver is mostly to solve a race problem. By the time the guard authorize the route, the component is trying to access the profile data from the store. Unfortunately, the reducer hasn't updated the store yet. With this resolver, the route is activated after the reducer has updated the store. Then the component can access the profile data from the store. – Richard77 Sep 09 '21 at 13:50