【问题标题】:How do I Mock RouterStateSnapshot for a Router Guard Jasmine test如何为 Router Guard Jasmine 测试模拟 RouterStateSnapshot
【发布时间】:2016-11-29 16:18:30
【问题描述】:

我有一个简单的路由器保护,我正在尝试测试canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot )。我可以像 new ActivatedRouteSnapshot() 这样创建 ActivatedRouteSnapshot,但我不知道如何创建模拟的 RouterStateSnapshot

根据我尝试过的代码...

let createEmptyStateSnapshot = function(
    urlTree: UrlTree, rootComponent: Type<any>){
    const emptyParams = {};
    const emptyData = {};
    const emptyQueryParams = {};
    const fragment = '';
    const activated = new ActivatedRouteSnapshot();
    const state = new RouterStateSnapshot(new TreeNode<ActivatedRouteSnapshot>(activated, []));
    return {
        state: state,
        activated: activated
    }
}

但是import {TreeNode} from "@angular/router/src/utils/tree"; 似乎需要被转译之类的,因为我得到...

未捕获的语法错误:意外的令牌导出 在 webpack:///~/@angular/router/src/utils/tree.js:8:0

【问题讨论】:

    标签: angular jasmine angular2-routing


    【解决方案1】:

    我设法做的略有不同,但它应该适合你:

    ...
    
    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);
      })
     ...
    

    【讨论】:

      【解决方案2】:

      我需要在路由中获取数据来测试我的后卫中的用户角色,所以我这样嘲笑它:

      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);
        });
      });
      

      【讨论】:

        【解决方案3】:

        如果目的只是将模拟传递给警卫,则没有必要使用 createSpyObj,如其他答案中所建议的那样。最简单的解决方案是仅模拟必需的字段,这些字段由您的警卫的canActivate 方法使用。此外,最好在解决方案中添加类型安全:

        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);
            ...
        });
        

        如果你不使用状态快照,只需传递null 代替

            const result = guard.canActivate(route, null as any);
        

        【讨论】:

        • 过去我们经常使用 null 技巧。但是,当使用启用了更严格类型检查的 Angular 11 时——现在是在创建项目时使用 Angular CLI 的一个选项——我们不能为 RouterStateSnapshot 传递空值。我最终做了{} as RouterStateSnapshot
        【解决方案4】:

        根据我之前关于路由器的问题,我尝试了这个...

        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)
        );
        

        我现在遇到的问题是我需要这里的 toString mockSnapshot = jasmine.createSpyObj("RouterStateSnapshot", ['toString']);。这是因为 jasmine createSpyObj 至少需要一个模拟方法。由于我没有测试 RouterStateSnapshot 的副作用,因此这似乎是白费力气。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-10-13
          • 2013-10-27
          • 1970-01-01
          • 1970-01-01
          • 2017-05-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多