Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test angular guard with observable subscription in constructor

I'm trying to unit test an angular guard which pipes an observable that belongs to an authentication service. The subscription happens in the guard canActivate() method.

I'm using a jasmine spy on the authentication service observable to return values, but the spy is never called in my unit test.

When testing a component, I use fixture.detectChanges(), but for this guard I can't find a way to test that it returns the right thing according to the observable value.

auth-guard.ts:

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {
  constructor(
    private authService: AuthService,
    private router: Router
  ) {}

  canActivate(): Observable<boolean> {
    return this.authService.isAuthenticated$.pipe(
      map(e => {
        if (e) {
          return true;
        }
      }),
      catchError((err) => {
        this.router.navigate(['/login']);
        return of(false);
      })
    );
  }
}

auth.service.ts:

@Injectable()
export class AuthService {

  private isAuthenticated = new BehaviorSubject<boolean>(false);

  get isAuthenticated$(): Observable<boolean> { return this.isAuthenticated.asObservable(); }

  ...
}

auth-guard.spec.ts:

describe('Authuard', () => {

  let authGuard: AuthGuard;
  let authService: jasmine.SpyObj<AuthService>;

  beforeEach(() => {

    const authServiceSpy = jasmine.createSpyObj('AuthService', ['isAuthenticated$']);

    TestBed.configureTestingModule({
      providers: [
        AuthGuard,
        { provide: AuthService, useValue: authServiceSpy }
      ]
    });

    authGuard = TestBed.get(AuthGuard);
    authService = TestBed.get(AuthService);
  });

  it('should create', () => {
    expect(authGuard).toBeDefined();
  });

  /* This test fails */
  it('should return false when not authenticated', () => {
    authService.isAuthenticated$.and.returnValue(of(false));

    authGuard.canActivate().subscribe(canActivate => {
        expect(canActivate).toBe(false);
    });
  });
});

The second test fails with this.authService.isAuthenticated$.pipe is not a function. The value returned by the spy on isAuthenticated$ is not taken.

How can I test that the guard returns the right value when the observable value return by the authentication service changes? Is it possible to do it with jasmine spies?

like image 445
aneder Avatar asked Oct 28 '25 01:10

aneder


1 Answers

Try throwError as below:

import { throwError } from 'rxjs';

it('should return false when not authenticated', () => {
    spyOn(authService,'isAuthenticated$').and.returnValue(throwError('error'));

    authGuard.canActivate().subscribe(canActivate => {
        expect(canActivate).toBeFalsy();
});
like image 194
Shashank Vivek Avatar answered Oct 30 '25 19:10

Shashank Vivek