Первая проблема - при создании authStub
:
const authStub: AuthService = jasmine.createSpyObj('AuthService', ['getUserEntity', 'currentUser', 'canEditProfile']);
В этом случае вы добавляете currentUser
как метод, но не как свойство. Правильный способ создать jasmine spyObj как с методами, так и со свойствами :
const authStub = {
...jasmine.createSpyObj('authStub', ['getUserEntity', 'canEditProfile']),
currentUser: of(<any>{ uid: 'jdkffdjjfdkls', role: Role.USER })
} as jasmine.SpyObj<AuthService>;
Обратите внимание , что в вашем примере - эта мутация объекта внутри теста ни на что не влияет:
authStub.currentUser = of(<any>{uid: 'jdkffdjjfdkls', role: Role.USER});
Причина в том, что вы используете useValue
при предоставлении услуг для TestBed
, и это означает, что тесты уже получили экземпляр службы аутентификации, который не имеет свойства currentUser
. Вот почему важно инициализировать его перед запуском метода configureTestingModule
.
Вторая проблема - поскольку ваш защитный код асинхронный, вы должны писать свои модульные тесты асинхронно (вы можете использовать done
, sync
или fakeAsync&tick
).
Вот окончательное решение:
describe('RouterGuardService', () => {
const routerStub: Router = jasmine.createSpyObj('Router', ['navigate']);
const authStub = {
...jasmine.createSpyObj('authStub', ['getUserEntity', 'canEditProfile']),
currentUser: of(<any>{ uid: 'jdkffdjjfdkls', role: Role.USER })
} as jasmine.SpyObj<AuthService>;
const notificationStub: NotificationService = jasmine.createSpyObj('NotificationService', ['danger']);
let profileGuardService: ProfileGuard;
function createInputRoute(url: string): ActivatedRouteSnapshot {
// ...
}
beforeEach(() => {
TestBed.configureTestingModule({
// ...
});
profileGuardService = TestBed.get(ProfileGuard);
});
it('should redirect to user overview - if has not permission', fakeAsync(() => {
(<jasmine.Spy>authStub.canEditProfile).and.returnValue(false);
(<jasmine.Spy>authStub.getUserEntity).and.returnValue(of({ uid: 'jdkffdjjfdkls', role: Role.USER }));
const spy = (<jasmine.Spy>routerStub.navigate).and.callFake(() => Promise.resolve());
const notifySpy = (<jasmine.Spy>notificationStub.danger).and.stub();
const url: ActivatedRouteSnapshot = createInputRoute('/profile/BBB/edit');
let expectedRes;
profileGuardService.canActivate(url).subscribe(res => {
expectedRes = res;
}, err => console.log(err));
tick();
expect(spy).toHaveBeenCalledWith(['/profile/BBB']);
expect(notifySpy).toHaveBeenCalledWith('Access denied. Must have permission to edit profile.');
expect(expectedRes).toBe(false);
}));
});
Если вы хотите иметь разные currentUser
для каждого теста динамически, вы можете сделать этот трюк - инициализировать свойство currentUser
в authStub
с BehaviorSubject:
const authStub = {
...jasmine.createSpyObj('authStub', ['getUserEntity', 'canEditProfile']),
currentUser: new BehaviorSubject({})
} as jasmine.SpyObj<AuthService>;
А затем внутри самих модульных тестов вы можете вызвать метод next
, чтобы установить необходимый текущий пользовательский макет:
it('should redirect to user overview - if has not permission', fakeAsync(() => {
(<BehaviorSubject<any>>authStub.currentUser).next(<any>{ uid: 'jdkffdjjfdkls', role: Role.USER });
// ...