Я использую Angular Universal. У меня есть охранник для маршрута, который ведет себя по-разному в зависимости от того, работаю ли я на сервере или на платформе браузера. Вот охранник:
export class UniversalShellGuard implements CanActivate {
private isBrowser: boolean;
constructor(@Inject(PLATFORM_ID) private platformId: Object) {
console.info('PLATFORM_ID = ' + platformId);
this.isBrowser = isPlatformBrowser(this.platformId);
}
canActivate(): Observable<boolean> | Promise<boolean> | boolean {
return !this.isBrowser;
}
}
Как видите, охранник вводит PLATFORM_ID и использует его, чтобы определить, является он canActivate() или нет.
Теперь я хотел написать простой модульный тест для сторожа и сделал следующее:
describe('UniversalShellGuard', () => {
let guard: UniversalShellGuard;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
UniversalShellGuard,
// Idea: for now I just want to test the behaviour if I would be on the browser, so I would just use a fixed value for PLATFORM_ID
{ provide: PLATFORM_ID, useValue: PLATFORM_BROWSER_ID },
],
});
guard = TestBed.get(UniversalShellGuard);
});
it('should deny', () => {
expect(guard.canActivate()).toBe(false);
});
});
Но это дает следующую ошибку:
ERROR in ./src/app/universal-shell.guard.spec.ts
Module not found: Error: Can't resolve '@angular/common/src/platform_id' in '/my-app-path/src/app'
@ ./src/app/universal-shell.guard.spec.ts 4:0-70 11:50-69
@ ./src sync \.spec\.ts$
@ ./src/test.ts
Я даже попробовал простую и прямолинейную конструкцию ограждения, не используя угловой TestBed:
it('should deny', () => {
const guard = new UniversalShellGuard(PLATFORM_BROWSER_ID);
expect(guard.canActivate()).toBe(false);
});
Та же ошибка.
Есть ли способ предоставить фиксированное значение для PLATFORM_ID для правильного модульного тестирования такой защиты?





Значение PLATFORM_BROWSER_ID не является частью общедоступного API, поэтому оно импортирует его из глубокого пути, это не разрешено. Вместо этого вы можете просто поставить 'browser':
{ provide: PLATFORM_ID, useValue: 'browser' },
Для любой другой платформы вы можете использовать значения эти:
export const PLATFORM_BROWSER_ID = 'browser';
export const PLATFORM_SERVER_ID = 'server';
export const PLATFORM_WORKER_APP_ID = 'browserWorkerApp';
export const PLATFORM_WORKER_UI_ID = 'browserWorkerUi';
Хороший. Я также столкнулся с этой проблемой в тесте модуля защиты, и в итоге мой код configureTestingModule был заключен в функцию configure(), которая принимает идентификатор платформы в качестве параметра и использует его в provide. Функция configure() вызывается кодом beforeEach в блоке describe с тестом SSR и другим блоком describe с обычными тестами браузера.
Другой способ провести тест - это имитировать результат isPlatformBrowser. Для этого вы должны создать оболочку этой функции:
export class UniversalShellGuard implements CanActivate {
...
private isBrowser(): boolean {
return isPlatformBrowser(this.platformId);
}
}
А затем, используя жасмин шпион, вы можете имитировать возвращаемое значение вашего метода isBrowser():
describe('UniversalShellGuard', () => {
let guard: UniversalShellGuard;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
UniversalShellGuard,
{ provide: PLATFORM_ID, useValue: '' },
],
});
guard = TestBed.get(UniversalShellGuard);
});
it('should deny', () => {
spyOn(guard, 'isBrowser').and.returnValue(true);
expect(guard.canActivate()).toBe(false);
});
});
isBrowser - это частный метод, вы получите ошибку в коде TS, потому что вы не можете получить доступ к частным методам и шпионить за ними.
Это глупо, но работает ... Метод
isPlatformBrowserдолжен получать объект, но насмешка со строкой - это нормально.