Reputation: 12660
Although I have been writing Angular 2 for a while now, I am only just writing my first Jasmine tests and have run into a little difficulty. I am trying to test that the CanActivate
method of service implementing CanActivate
is behaving itself, and is returning true
or false
as expected.
My method looks like this:
canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Observable<boolean> {
return this.store$
.map( ( store: StoreState ) => store.currentUser )
.first()
.map( ( user ) => {
if ( user.isAuthenticated ) {
return true;
}
// TODO: This needs refactoring. Need to provide RouterStateSnapshot in test,
// rather than ignoring it!
this.redirectUrl = state ? state.url : '';
this.injector.get( Router ).navigate( ['/login'] );
return false;
} );
}
An extract of my test looks like this:
service = TestBed.get( AuthGuardService );
it( 'should prevent navigation', () => {
service.canActivate(null, null).subscribe((res) => expect( res ).toBeTruthy() );
} );
How do I mock/stub/whatever the second parameter of my call to service.canActivate
, rather than simply passing in null?
Upvotes: 1
Views: 5192
Reputation: 30089
describe('AuthGuard', () => {
let mockSnapshot: RouterStateSnapshot;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
// so we can get the Router injected
RouterTestingModule,
// other imports as needed
],
// usual config here
});
// create a jasmine spy object, of the required type
// toString is because we have to mock at least one method
mockSnapshot = createSpyObj<RouterStateSnapshot>('RouterStateSnapshot', ['toString']);
});
it('should prevent non-authenticated access',
async(inject([AuthGuard, AuthService, Router], (guard: AuthGuard, auth: AuthService, router: Router) => {
// ensure we're logged out
auth.logout();
// set the url on our mock snapshot
mockSnapshot.url = '/protected';
// so we can spy on what's been called on the router object navigate method
spyOn(router, 'navigate');
expect(guard.canActivate(null, mockSnapshot)).toBeFalsy();
// check that our guard re-directed the user to another url
expect(router.navigate).toHaveBeenCalled();
})));
});
})
Upvotes: 2
Reputation: 179
Here is my solution which I used for unit testing of Custom Router State Serializer
custom-serializer.ts
import { RouterStateSerializer } from '@ngrx/router-store';
import { RouterStateSnapshot, Params } from '@angular/router';
/**
* The RouterStateSerializer takes the current RouterStateSnapshot
* and returns any pertinent information needed. The snapshot contains
* all information about the state of the router at the given point in time.
* The entire snapshot is complex and not always needed. In this case, you only
* need the URL and query parameters from the snapshot in the store. Other items could be
* returned such as route parameters and static route data.
*/
export interface RouterStateUrl {
url: string;
params: Params;
queryParams: Params;
}
export class CustomRouterStateSerializer
implements RouterStateSerializer<RouterStateUrl> {
serialize(routerState: RouterStateSnapshot): RouterStateUrl {
let route = routerState.root;
while (route.firstChild) {
route = route.firstChild;
}
const { url, root: { queryParams } } = routerState;
const { params } = route;
// Only return an object including the URL, params and query params
// instead of the entire snapshot
return { url, params, queryParams };
}
}
custom-serializer.spec.ts
import { CustomRouterStateSerializer } from './utils';
import { RouterStateSnapshot } from '@angular/router';
describe('Utils CustomRouterStateSerializer', () => {
let mockSnapshot: RouterStateSnapshot;
let serializer: CustomRouterStateSerializer;
let mockSnapshotProxy;
beforeEach(() => {
mockSnapshot = jasmine.createSpyObj<RouterStateSnapshot>('RouterStateSnapshot', ['toString']);
serializer = new CustomRouterStateSerializer();
});
it('should serialize RouterStateSnapshot to subset of params', () => {
mockSnapshotProxy = new Proxy(mockSnapshot, {
get(target, prop) {
if (prop === 'root') {
return {
params: {
id: 100
},
queryParams: {
name: 'John'
}
};
} else if (prop === 'url') {
return '/orders';
}
},
});
const result = serializer.serialize(mockSnapshotProxy);
expect(result.url).toBe('/orders');
expect(result.params.id).toBe(100);
expect(result.queryParams.name).toBe('John');
});
});
I used jasmine.createSpyObj to create object with proper type and Proxy to pass in required properties
Upvotes: 0