Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing Angular 2 authGuard; spy method is not being called

I'm trying to unit test my auth guard service. From this answer I was able to get this far, but now when I run the unit test for this, it says Expected spy navigate to have been called.

How to I get my spied router to be used as this.router in the service?

auth-guard.service.ts

import { Injectable } from '@angular/core';
import { Router, CanActivate } from '@angular/router';

@Injectable()
export class AuthGuardService {

  constructor(private router:Router) { }

  public canActivate() {
    const authToken = localStorage.getItem('auth-token');
    const tokenExp = localStorage.getItem('auth-token-exp');
    const hasAuth = (authToken && tokenExp);

    if(hasAuth && Date.now() < +tokenExp){
      return true;
    }
    this.router.navigate(['/login']);
    return false;
  }
}

auth-guard.service.spec.ts

import { TestBed, async, inject } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';

import { AuthGuardService } from './auth-guard.service';

describe('AuthGuardService', () => {
  let service:AuthGuardService = null;
  let router = {
    navigate: jasmine.createSpy('navigate')
  };

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        AuthGuardService,
        {provide:RouterTestingModule, useValue:router}
      ],
      imports: [RouterTestingModule]
    });
  });

  beforeEach(inject([AuthGuardService], (agService:AuthGuardService) => {
    service = agService;
  }));

  it('checks if a user is valid', () => {
    expect(service.canActivate()).toBeFalsy();
    expect(router.navigate).toHaveBeenCalled();
  });
});

Replacing RouterTestingModule with Router like in the example answer throws Unexpected value 'undefined' imported by the module 'DynamicTestModule'.

like image 998
coblr Avatar asked Jan 05 '17 22:01

coblr


People also ask

How does AuthGuard work in angular?

AuthGuard is used to protect the routes from unauthorized access in angular. How AuthGuard Works? Auth guard provide lifecycle event called canActivate. The canActivate is like a constructor.

What is Spy in angular testing?

What are Spies? Spy is a feature in Jasmine that allows you to spy on something to achieve the following goals: Monitor if a function is called along with the parameters pass to it. Override function return values or properties to simulate desired situations during tests.

How do I test a router navigate in angular 8?

We can test routing in Angular by using RouterTestingModule instead of RouterModule to provide our routes. This uses a spy implementation of Location which doesn't trigger a request for a new URL but does let us know the target URL which we can use in our test specs.


2 Answers

Instead of stubbing Router, use dependency injection and spy on the router.navigate() method:

import { TestBed, async, inject } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { Router } from '@angular/router';

import { AuthGuardService } from './auth-guard.service';

describe('AuthGuardService', () => {

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

  it('checks if a user is valid',

    // inject your guard service AND Router
    async(inject([AuthGuardService, Router], (auth, router) => {

      // add a spy
      spyOn(router, 'navigate');

      expect(auth.canActivate()).toBeFalsy();
      expect(router.navigate).toHaveBeenCalled();
    })
  ));
});

https://plnkr.co/edit/GNjeJSQJkoelIa9AqqPp?p=preview

like image 137
stealththeninja Avatar answered Sep 28 '22 00:09

stealththeninja


For this test, you can use the ReflectiveInjector to resolve and create your auth-gaurd service object with dependencies.

But instead of passing the actual Router dependency, provide your own Router class (RouterStub) that has a navigate function. Then spy on the injected Stub to check if navigate was called.

import {AuthGuardService} from './auth-guard.service';
import {ReflectiveInjector} from '@angular/core';
import {Router} from '@angular/router';

describe('AuthGuardService', () => {
    let service;
    let router;
    beforeEach(() => {
        let injector = ReflectiveInjector.resolveAndCreate([
            AuthGuardService,
            {provide: Router, useClass: RouterStub}
        ]);
        service = injector.get(AuthGuardService);
        router = injector.get(Router);
    });

    it('checks if a user is valid', () => {
        let spyNavigation = spyOn(router, 'navigate');
        expect(service.canActivate()).toBeFalsy();
        expect(spyNavigation).toHaveBeenCalled();
        expect(spyNavigation).toHaveBeenCalledWith(['/login']);
    });
});

class RouterStub {
        navigate(routes: string[]) {
             //do nothing
        }
}
like image 27
qdivision Avatar answered Sep 28 '22 01:09

qdivision