问题
I have a simple router guard and I am trying to test the canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot )
. I can create the ActivatedRouteSnapshot like this new ActivatedRouteSnapshot()
but I cannot figure out how to create a mocked RouterStateSnapshot
.
Per the code I tried...
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
}
}
But import {TreeNode} from "@angular/router/src/utils/tree";
seems to need to be transpiled or something because I get...
Uncaught SyntaxError: Unexpected token export at webpack:///~/@angular/router/src/utils/tree.js:8:0 <- test.bundle.ts:72431
回答1:
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);
})
...
回答2:
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);
});
});
回答3:
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.
回答4:
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);
来源:https://stackoverflow.com/questions/40870670/how-do-i-mock-routerstatesnapshot-for-a-router-guard-jasmine-test