angularangular-ui-routerangular6angular7

How to unit test canActivate of Angular?


How can I test canActivate function of angular that returns a function which in turn returns a boolean value?.

I tried creating objects of ActivatedrouterSnapshot and routerStateSnapshot and passing it to canActivate function but that didn't help.

export class AuthGuard implements CanActivate {
    constructor(
        private authService: AuthenticationService,
        private loginService: LoginService,
        private router: Router
    ) {}

    canActivate(
        next: ActivatedRouteSnapshot,
        state: RouterStateSnapshot
    ): Observable<boolean> | Promise<boolean> | boolean {
        return this.checkLogin(state.url);
    }

    checkLogin(url: string): boolean {
    if (this.authService.isLoggedIn()) {  return true; }

    // Store the attempted URL for redirecting
    this.loginService.redirectUrl = url;

    // Navigate to the login page with extras
    this.router.navigate(['/login']);
        return false;
    }
}

Since the checklogin returns true,I want that to happen. But I don't know where to start?.


Solution

  • There are a lot of ways to do this. I would suggest something like the following. Just to show some variety I mocked one service with a class and another service with a spyObject.

    Here is the suggested code:

    class LoginMock implements Partial<LoginService> {
        redirectUrl: string;
    }
    
    describe('AuthGuard', () => {
    
        let authGuard: AuthGuard;
        let loginService: LoginMock;
        const routerMock = jasmine.createSpyObj('Router', ['navigate']);
        const authMock = jasmine.createSpyObj('AuthenticationService', ['isLoggedIn']);
    
        beforeEach(() => {
            loginService = new LoginMock();
            authGuard = new AuthGuard(authMock, loginService, routerMock);
        });
    
        it('should be createable', () => expect(authGuard).toBeTruthy());
    
        it('should return true for canActivate() and not set loginService.redirectUrl when isLoggedIn === true', ()=> {
            authMock.isLoggedIn.and.returnValue(true);
            const result = authGuard.canActivate(new ActivatedRouteSnapshot(), <RouterStateSnapshot>{url: 'testUrl'});
            expect(result).toBe(true);
            expect(loginService.redirectUrl).toBeUndefined();
        });
    
        it('should return false for canActivate() and set loginService.redirectUrl when isLoggedIn === false', ()=> {
            authMock.isLoggedIn.and.returnValue(false);
            const result = authGuard.canActivate(new ActivatedRouteSnapshot(), <RouterStateSnapshot>{url: 'testUrl'});
            expect(result).toBe(false);
            expect(loginService.redirectUrl).toEqual('testUrl');
        });
    
    });
    

    I have put this together in a Stackblitz for you. Feel free to fork that into your own Stackblitz environment and modify.

    Best of luck. :)