angulartestingreduxguardkarma-coverage

Angular - How to test Guards with ActivatedRouteSnapshot RouterStateSnapshot and GlobalState


I'm creating unit test for my Angular app but i don'k know create tests for my custom guard that use Redux.

This is my code of the guard

import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, CanLoad, Route, Router, RouterStateSnapshot, UrlSegment, UrlTree } from '@angular/router';
import { first, map, Observable, tap } from 'rxjs';
import { GlobalState } from '../state/app.reducer';
import { Store } from '@ngrx/store';
import { RolesAccount } from 'src/app/pages/auth/interfaces/auth.constant';
 
@Injectable({
  providedIn: 'root'
})
export class AdministratorGuard implements CanActivate, CanLoad {
 
  constructor(private route: Router,private store: Store<GlobalState>) {}
 
  canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return this.isAdmin()
  }
  canLoad( route: Route, segments: UrlSegment[]): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return this.isAdmin()
  }
 
  isAdmin():Observable<true | UrlTree>{    
    return this.store.select('authentication').pipe( 
      first(), // take of first value
      map(userStore => userStore.userLogged?.role || ''), 
      //TODO we hardcode the email of administrator until role is in JWT
      map(role => role === RolesAccount.ADMIN ? true : this.route.createUrlTree(['/home']))
    );    
  }
}

I run npm run coverage

And this is the uncovered block (I need create unit test for canActivate, canLoad, and isAdmin

Uncovered block

And this is my unit testing file (default testing)

import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { AuthComponent } from 'src/app/pages/auth/auth.component';
import { IUserState } from 'src/app/pages/auth/interfaces/auth.interfaces';

import { AdministratorGuard } from '../administrator.guard';

fdescribe('AdministratorGuard', () => {
  let guard: AdministratorGuard;
  let store: MockStore<IUserState>;  

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports:[
        RouterTestingModule.withRoutes(
          [
            {
              path: 'auth',
              component: AuthComponent
            },
          ]
        ),
      ],
      providers:[
        provideMockStore({}),
      ]
    });
    guard = TestBed.inject(AdministratorGuard);
    store = TestBed.inject(MockStore);
  });

  it('should be created', () => {
    expect(guard).toBeTruthy();
  });
});

Thanks in advance


Solution

  • Thanks Wesley for your comment. Here's the solution

    This is my code of guard

    import { Injectable } from '@angular/core';
    import { ActivatedRouteSnapshot, CanActivate, CanLoad, Route, Router, RouterStateSnapshot, UrlSegment, UrlTree } from '@angular/router';
    import { first, map, Observable, tap } from 'rxjs';
    import { GlobalState } from '../state/app.reducer';
    import { Store } from '@ngrx/store';
    import { RolesAccount } from 'src/app/pages/auth/interfaces/auth.constant';
    
    @Injectable({
      providedIn: 'root'
    })
    export class AdministratorGuard implements CanActivate, CanLoad {
    
      constructor(private route: Router,private store: Store<GlobalState>) {}
    
      canActivate(): Observable<true | UrlTree> {
        return this.isAdmin()
      }
      canLoad(): Observable<true | UrlTree>{
        return this.isAdmin()
      }
    
      isAdmin():Observable<true | UrlTree>{    
        return this.store.select('authentication').pipe( 
          first(), // take of first value
          map(userStore => userStore?.userLogged?.role || ''), 
          //TODO we hardcode the email of administrator until role is in JWT
          map(role => role === RolesAccount.ADMIN ? true : this.route.createUrlTree(['/home']))
        );    
      }
    }
    

    And this is the test

    import { TestBed } from '@angular/core/testing';
    import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
    import { RouterTestingModule } from '@angular/router/testing';
    import { MockStore, provideMockStore } from '@ngrx/store/testing';
    import { lastValueFrom, of } from 'rxjs';
    import { AuthComponent } from 'src/app/pages/auth/auth.component';
    import { RolesAccount } from 'src/app/pages/auth/interfaces/auth.constant';
    import { IUserState } from 'src/app/pages/auth/interfaces/auth.interfaces';
    
    import { AdministratorGuard } from '../administrator.guard';
    
    describe('AdministratorGuard', () => {
      let guard: AdministratorGuard;
      let store: MockStore<IUserState>;  
      let defaultState:IUserState = {
        authentication:{
          userLogged:{
            name:'',
            email:'',
            phone:'',
            accessToken:'',
            refreshToken:'',
            role: RolesAccount.USER
          },  
        }  
      }
      beforeEach(() => {
        const routerStub = {
          events: of('/'),
          createUrlTree: (commands: any, navExtras = {}) => {}
        };
        TestBed.configureTestingModule({
          imports:[
            RouterTestingModule.withRoutes(
              [
                {
                  path: 'auth',
                  component: AuthComponent
                },
              ]
            ),
          ],
          providers:[
            provideMockStore({
              initialState:defaultState
            }),
            { provide: Router, useValue: routerStub}        
          ]
        });
        guard = TestBed.inject(AdministratorGuard);
        store = TestBed.inject(MockStore);
    
      });
    
      it('should be created', () => {
        expect(guard).toBeTruthy();
      });
    
      it('can Activate to be True ', () => {
        const storeSpy = spyOn(store, 'select').and.callThrough();
        guard.canActivate()
        expect(storeSpy).toHaveBeenCalledTimes(1);
      });
    
      it('can canLoad to be True ', () => {
        const storeSpy = spyOn(store, 'select').and.callThrough();
        guard.canLoad()
        expect(storeSpy).toHaveBeenCalledTimes(1);
      })
    
      it('validate role ADMIN',async () => {
        const nextState:IUserState = {  
          authentication:{
            userLogged:{
              name:'Test',
              email:'mailTest@gmail.com',
              phone:'+5411557788',
              accessToken:'asfksakmfaskmfsakm',
              refreshToken:'safla25l4235lllfs',
              role: RolesAccount.ADMIN
            },    
          }     
        }
    
        store.setState(nextState);
    
        const isAdmin = await lastValueFrom(guard.isAdmin())
    
        expect(isAdmin).toBeTrue()
      })
    
      it('if is not admin,navigate home',async () => {
        const nextState:IUserState = {  
          authentication:null
        }
    
        store.setState(nextState);
    
        const isAdmin = await lastValueFrom(guard.isAdmin())
    
        expect(isAdmin).toBeUndefined()
      })
    });