Search code examples
unit-testingwebjasminekarma-jasminedelay

Jasmine Karma testing function with delay()


I have an angular class like so (angular 8)

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {

  constructor(private router: Router, private loginService: LoginService) { }

  private  authenticated = false;
  private  retry = 0;
 
  canActivate(route: ActivatedRouteSnapshot,router: RouterStateSnapshot): Observable<boolean> | boolean {

    if (this.authenticated) {
      return true;
    }

    return of(null).pipe(
        delay(this.retry++ >0 ? 5000 : 0), //start delay on second retry for debouncing request
        flatMap(() =>
           this.loginService.isLoggedIn().pipe(
            map(result => {
              this.authenticated = result;
              return this.authenticated;
            }))
        )
      );
  }

}

I am trying to test it as follows:

describe('AuthGuard', () => {

  let service: LoginService;
  let guard: AuthGuard;
  let router: Router;
//left out other irrelevant fields here
  
  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule, RouterTestingModule],
      providers: [LoginService]
    });
    router = TestBed.inject(Router);
    service = TestBed.inject(LoginService);
    http = TestBed.inject(HttpClient);
    httpClientMock = TestBed.inject(HttpTestingController);
    guard = TestBed.inject(AuthGuard);
  });

  it('should return false for canActivate() if user is not logged in',()=> {
    spyOn(service, 'isLoggedIn').and.returnValue(of(false));

    const result = guard.canActivate(new ActivatedRouteSnapshot(), <RouterStateSnapshot>{url: 'testUrl'});

    if (isObservable(result)) {
      console.log("observable!")
      result.subscribe(
        res => {
          console.log("res is " + res)
          expect(res).toBeFalsy("Did not get FALSE for user that is not logged in")
        })
    }
  });

I also have another identical test for when the user is logged in - (isLoggedIn = true) However it seems that (probably because of delay), the subscribe function isn't being called. I see the log "observable!" but not "res is {res}". i tried using fakeAsync and all sorts of stuff, nothing really worked.

Would really appreciate the help! thanks

(if I comment out the "delay" in the original function the tests work well) Likewise, i don't have the option of changing the original canActivate function


Solution

  • Try this:

    it('should return false for canActivate() if user is not logged in', fakeAsync(()=> {
        spyOn(service, 'isLoggedIn').and.returnValue(of(false));
    
        const result = guard.canActivate(new ActivatedRouteSnapshot(), <RouterStateSnapshot>{url: 'testUrl'});
    
        if (isObservable(result)) {
          console.log("observable!");
          let result; // get a variable here to assign to result
          const subscription = result.subscribe(
            res => {
              console.log("res is " + res)
              expect(res).toBeFalsy("Did not get FALSE for user that is not logged in")
              result = res; // assign to result
            });
          tick(5000);
          expect(result).toBe(false);
          subscription.unsubscribe(); // unsubscribe from the subscription
        }
      }));
    

    Check this link out.