简体   繁体   中英

Minimal mock for unit testing angular Router and CanActivate guard

import {Router, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree} from '@angular/router';

@Injectable({provideIn: 'root'})
export class FooGuard implements CanActivate {
  constructor (private readonly router: Router) {}

  canActivate (next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<UrlTree> {
    const xxx = myMagic(next); // irrelevant app logic that depends on next url only

    return (async () => this.router.parseUrl(xxx));
  }
}

Trying to find an example of testing code for this piece without a page of extra boilerplate. Hopefully can get something closer to 5-6 lines of code per mock. Need:

  • Mock for Router
  • Mock for ActivatedSnapshot

Take a look at RouterTestingModule . It's not a six lines of code solution, but a pretty compact one. I think it's the best way to test guards and routes:

import { Component, Injectable } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from "@angular/router";
import { RouterTestingModule } from '@angular/router/testing';

@Injectable({
    providedIn: 'root'
})
export class FooGuard implements CanActivate {
    constructor (private readonly router: Router) {}

    canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): UrlTree {
      const url = "some/new/url"; // create an url based on next snapshot
      return this.router.parseUrl(url);
    }
  }

@Component({ template: '' })
export class DummyComponent {}

function setup(): {
    router: Router
} {
    TestBed.configureTestingModule({
        imports: [
            RouterTestingModule.withRoutes([
                { path: 'test', component: DummyComponent, canActivate: [ FooGuard ] },
                { path: 'some/new/url', component: DummyComponent }
            ])
        ],
        declarations: [
            DummyComponent
        ]
    });

    return {
        router: TestBed.get(Router)
    };
}

describe(FooGuard.name, () => {
    it('should redirect to a new url', async () => {
        const { router } = setup();
        await router.navigateByUrl('/test');
        expect(router.url).toBe('/some/new/url');
    })
});


Actually the regular Router.forRoot() should also work in this case, but RouterTestingModule must be more suitable for testing. For example the last one provides custom Location implementation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM