I built a small angular app and now I'm writing unit tests. So far so good, but when I try to test my authGuard I ran into some problems. I am using Spectator. I provide the platformId in the providers section in my spec, but I want to be able to overwrite it so I can test scenarios where for values 'server' as well as 'browser' My code is for the authGuard:
@Injectable({
providedIn: 'root',
})
export class AuthGuard implements CanActivate {
constructor(
private authService: AuthService,
private router: Router,
@Inject(PLATFORM_ID) private platformId: Object,
) {}
canActivate(
_route: ActivatedRouteSnapshot,
_state: RouterStateSnapshot,
): boolean | UrlTree | Promise<boolean | UrlTree> | Observable<boolean | UrlTree> {
if (isPlatformServer(this.platformId)) {
console.log('server');
return true;
} else {
console.log('browser');
this.authService.autoLogin();
return this.authService.user.pipe(
take(1),
map((user) => {
console.log('user', user);
if (!!user) {
return true;
}
console.log('navugate');
this.router.navigate(['/auth']);
return false;
}),
);
}
}
}
And the spec file:
describe('AuthGuard', () => {
let spectator: SpectatorService<AuthGuard>;
const config = {
service: AuthGuard,
imports: [RouterTestingModule, HttpClientTestingModule],
providers: [AppRoutingModule, AuthService, { provide: PLATFORM_ID, useValue: 'server' }],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
};
const createService = createServiceFactory(config);
// some other tests here ....
it('should should navigate to /auth if running on browser and not authenticated', () => {
config.providers = [AppRoutingModule, { provide: PLATFORM_ID, useValue: 'browser' }];
spectator = createService();
const spyNavigate = spyOn(spectator.service['router'], 'navigate').and.callThrough();
const user = new User('[email protected]', 'id', 'token', new Date(10000));
spectator.service['authService'].user.next(user);
spectator.service.canActivate(null, null);
expect(spyNavigate).toHaveBeenCalled();
});
});
Now when I run this overriding the config does not work. This is to be expected, since the config object was already passed on before altering it. But how to achieve my goal? I tried this:
describe('AuthGuard', () => {
let spectator: SpectatorService<AuthGuard>;
const config = {
service: AuthGuard,
imports: [RouterTestingModule, HttpClientTestingModule],
providers: [AppRoutingModule, AuthService, { provide: PLATFORM_ID, useValue: 'server' }],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
};
// some other tests here ....
it('should should navigate to /auth if running on browser and not authenticated', () => {
config.providers = [AppRoutingModule, { provide: PLATFORM_ID, useValue: 'browser' }];
const createService = createServiceFactory(config);
spectator = createService();
const spyNavigate = spyOn(spectator.service['router'], 'navigate').and.callThrough();
const user = new User('[email protected]', 'id', 'token', new Date(10000));
spectator.service['authService'].user.next(user);
spectator.service.canActivate(null, null);
expect(spyNavigate).toHaveBeenCalled();
});
});
But that gave me the error Error: 'beforeEach' should only be used in 'describe' function
which I found confusing since I am not using beforeEach in this spec.
I feel that some tool like Spectator should probably have an easy way to do this, it is not that exotic, right?
Any help is appreciated!
You can do this by passing arguments to
createService()
: