web-dev-qa-db-fra.com

Comment tester unitaire canActivate guard method of angular2 using Jasmine?

Désolé d'avoir posé ce type de question. Mais je ne suis pas en mesure de trouver de blog ou de didacticiels youtube sur l'écriture du test de fichier de garde canActivate. Il n'y a rien non plus dans la documentation officielle.

toute aide sera très appréciée.

20
Sumit Khanduri

puisque personne n'a répondu à ma question, je colle donc mon extrait de code pour la référence afin d'aider les personnes susceptibles de rencontrer cette situation.

sampleLoggedIn.guard.ts

import {Injectable} from '@angular/core';
import {Router, CanActivate} from '@angular/router';
import {StorageService} from '../storage.service';

@Injectable()
export class LoggedInGuard implements CanActivate {
    constructor(private router: Router, private storageService: StorageService) {
    }

    /**Overriding canActivate to guard routes
     *
     * This method returns true if the user is not logged in
     * @returns {boolean}
     */
    canActivate() {
        if (this.storageService.isLoggedIn) {
            return true;
        } else {
            this.router.navigate(['home']);
            return false;
        }
    }
}

sampleLoggedIn.guard.spec.ts

import {TestBed, async} from '@angular/core/testing';
import {FormsModule} from '@angular/forms';
import {HttpModule} from '@angular/http';
import {CommonModule} from '@angular/common';
import 'rxjs/Rx';
import 'rxjs/add/observable/throw';
import {Router} from '@angular/router';
import 'rxjs/add/operator/map';
import {LoggedInGuard} from './loggedin.guard';
import {StorageService} from '../storage.service';
import {CookieService} from 'angular2-cookie/core';

describe('Logged in guard should', () => {
    let loggedInGuard: LoggedInGuard;
    let storageService: StorageService;
    let router = {
        navigate: jasmine.createSpy('navigate')
    };

    // async beforeEach
    beforeEach(async(() => {
        TestBed.configureTestingModule({
            imports: [FormsModule, CommonModule, HttpModule],
            providers: [LoggedInGuard, StorageService, CookieService,
                {provide: Router, useValue: router}
            ]
        })
            .compileComponents(); // compile template and css
    }));

    // synchronous beforeEach
    beforeEach(() => {
        loggedInGuard = TestBed.get(LoggedInGuard);
        storageService = TestBed.get(StorageService);
    });

    it('be able to hit route when user is logged in', () => {
        storageService.isLoggedIn = true;
        expect(loggedInGuard.canActivate()).toBe(true);
    });

    it('not be able to hit route when user is not logged in', () => {
        storageService.isLoggedIn = false;
        expect(loggedInGuard.canActivate()).toBe(false);
    });
});
29
Sumit Khanduri

Cette question est assez ancienne - mais comme j'essayais de trouver moi-même une documentation détaillée sur les tests unitaires, je voulais juste mettre mon approche ici. En général, s'il y a des dépendances dans ma garde/service/composant/quoi que ce soit, je pense que toutes devraient être moquées et non les vrais services devraient être utilisés. Comme ces services ne sont pas ce que nous voulons tester dans notre test unitaire pour le gardien - nous voulons juste tester le gardien. Voici donc un exemple générique de la façon dont je le ferais pour un garde retournant un observable:

import { MyGuard } from './path/to/your/guard';
import { TestBed } from '@angular/core/testing';
import { finalize } from 'rxjs/operators';

describe('MyGuard Test', () => {
    const createMockRoute = (id: string) => {
    return {
      params: { id: id }
    } as any;
  };

  const createMockRouteState = () => null;
  let guard: MyGuard;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        MyGuard,
      ]
    });

    guard = TestBed.get(MyGuard);
  });

  it('should not be able to activate invalid route', done => {
    const route = createMockRoute(null);
    const state = createMockRouteState();
    const res$ = guard.canActivate(route, state);
    res$.pipe(finalize(done)).subscribe(res => expect(res).toBeFalsy());
  });
});

et c'est ce que je ferais dans votre cas spécifique (devrait fonctionner avec angular 6, canActivate devrait également prendre 2 paramètres):

import { LoggedInGuard } from './loggedin.guard';
import { TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';
import { StorageService } from '../storage.service';

describe('LoggedInGuard', () => {
  let guard: LoggedInGuard;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        LoggedInGuard,
        { provide: Router, useClass: { navigate: () => null } },
        { provide: StorageService, useClass: { } }
      ]
    });

    guard = TestBed.get(LoggedInGuard);
  });

  it('should not be able to activate when logged out', () => {
    const storageService = TestBed.get(StorageService);
    storageService.isLoggedIn = false;
    const res = guard.canActivate(null, null);
    expect(res).toBeFalsy();
  });

  it('should be able to activate when logged in', () => {
    const storageService = TestBed.get(StorageService);
    storageService.isLoggedIn = true;
    const res = guard.canActivate(null, null);
    expect(res).toBeTruthy();
  });
});
1
Jay