How do I Mock RouterStateSnapshot for a Router Guard Jasmine test

后端 未结 4 1066
生来不讨喜
生来不讨喜 2021-02-05 05:23

I have a simple router guard and I am trying to test the canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot ). I can create the ActivatedRouteSn

相关标签:
4条回答
  • 2021-02-05 05:41

    I managed to do it slightly differently but it should work for you :

    ...
    
    let mockSnapshot:any = jasmine.createSpyObj<RouterStateSnapshot>("RouterStateSnapshot", ['toString']);
    
    @Component({
      template: '<router-outlet></router-outlet>'
    })
    class RoutingComponent { }
    
    @Component({
      template: ''
    })
    class DummyComponent { }
    
    describe('Testing guard', () => {
      beforeEach(() => TestBed.configureTestingModule({
        imports: [
          RouterTestingModule.withRoutes([
            {path: 'route1', component: DummyComponent},
            {path: 'route2', component: DummyComponent},
            ...
          ])
      ],
      declarations: [DummyComponent, RoutingComponent],
      providers: [
        GuardClass,
        {provide: RouterStateSnapshot, useValue: mockSnapshot}
      ]
    }).compileComponents());
    
      it('should not allow user to overcome the guard for whatever reasons', 
        inject([GuardClass], (guard:GuardClass) => {
          let fixture = TestBed.createComponent(RoutingComponent);
          expect(guard.canActivate(new ActivatedRouteSnapshot(), mockSnapshot)).toBe(false);
      })
     ...
    
    0 讨论(0)
  • 2021-02-05 05:50

    If the purpose is just to pass the mock to the guard, it's not necessary to use createSpyObj, as suggested in other answers. The simplest solution is just to mock only required fields, which are used by canActivate method of your guard. Also, it would be better to add type safety to the solution:

    const mock = <T, P extends keyof T>(obj: Pick<T, P>): T => obj as T;
    
    it('should call foo', () => {
        const route = mock<ActivatedRouteSnapshot, 'params'>({
            params: {
                val: '1234'
            }
        });
    
        const state = mock<RouterStateSnapshot, "url" | "root">({
            url: "my/super/url",
            root: route // or another mock, if required
        });
    
        const guard = createTheGuard();
        const result = guard.canActivate(route, state);
        ...
    });
    

    If you don't use the state snapshot, just pass null instead

        const result = guard.canActivate(route, null as any);
    
    0 讨论(0)
  • 2021-02-05 05:55

    Based on a previous question I had about Router I tried this...

    let mockSnapshot: any;
    ...
    mockSnapshot = jasmine.createSpyObj("RouterStateSnapshot", ['toString']);
    ...
    TestBed.configureTestingModule({
      imports: [RouterTestingModule],
      providers:[
        {provide: RouterStateSnapshot, useValue: mockSnapshot}
      ]
    }).compileComponents();
    ...
    let test = guard.canActivate(
      new ActivatedRouteSnapshot(),
      TestBed.get(RouterStateSnapshot)
    );
    

    The problem I now have is that I need the toString here mockSnapshot = jasmine.createSpyObj("RouterStateSnapshot", ['toString']);. This is because jasmine createSpyObj requires at least one mocked method. Since I am not testing the side effects of RouterStateSnapshot, this seems like extra work for nothing.

    0 讨论(0)
  • 2021-02-05 05:57

    I needed to get the data in the route to test for user roles in my guard, so I mocked it this way:

    class MockActivatedRouteSnapshot {
        private _data: any;
        get data(){
           return this._data;
        }
    }
    
    describe('Auth Guard', () => {
       let guard: AuthGuard;
       let route: ActivatedRouteSnapshot;
    
       beforeEach(() => {
          TestBed.configureTestingModule({
             providers: [AuthGuard, {
                provide: ActivatedRouteSnapshot,
                useClass: MockActivatedRouteSnapshot
            }]
          });
          guard = TestBed.get(AuthGuard);
      });
    
      it('should return false if the user is not admin', () => {
         const expected = cold('(a|)', {a: false});
    
         route = TestBed.get(ActivatedRouteSnapshot);
         spyOnProperty(route, 'data', 'get').and.returnValue({roles: ['admin']});
    
         expect(guard.canActivate(route)).toBeObservable(expected);
      });
    });
    
    0 讨论(0)
提交回复
热议问题